摘要:要達到想要的效果正確的做法是即用來產生一個立即作用域,保證回調函數執行的時候最近的原型作用域的就是當時循環的。判斷數組中是否存在滿足條件的項,只要有一項滿足條件,就會返回。
之前常常因為不注意,習慣用寫PHP或者Java的方式來寫nodejs,產生了了一些錯誤,這里總結一些小小的trick,以便于展示nodejs的不同,和平時需要注意的地方。
變量提升var variable = "global"; console.log(variable); function fn () { console.log(variable); var variable = "local"; console.log(variable); } fn();
你可能以為這段代碼執行結果為:
global global local
但實際上結果是
global undefined local
原因就是函數作用域導致局部變量在整個函數體內部可見,所以執行起來就成了:
function fn () { var variable console.log(variable); variable = "local"; console.log(variable); }
函數內部的console.log出于就近原則讀取的是內部variable,亦即局部variable覆蓋了全局variable,然后局部variable是整個函數體內可見,所以相當于提升了變量聲明,亦即變量聲明放在了函數開頭,但是變量初始化還是在原來的位置,所以就是上面展示的順序。
寫Java的時候我們傾向于在最開始使用一個局部變量之前聲明它,這樣幫我們清晰它的作用域以及生命周期;但是JavaScript沒有塊級作用域,所以局部變量最好寫在函數開始,這樣才能更清晰的展示它的作用域(整個函數內部)和生命周期,避免產生誤解。
有點需要注意的是:聲明寫var與不寫var是有區別的:
console.log(a); a = 1;
會報錯,而下面這個:
console.log(a); var a = 1;
結果是 undefined ,也就是沒有var的聲明不會提升。
函數提升js中創建函數有兩種方式:函數聲明式和函數字面量式。只有函數聲明才存在函數提升:
console.log(f1); console.log(f2); function f1() {} var f2 = function() {}
結果:
[Function: f1] undefined
就是函數提升導致順序變為:
function f1() {} console.log(f1); console.log(f2); var f2 = function() {}原型繼承中的坑
JavaScript 沒有 提供對象繼承的語言級別特性,而是通過原型復制來實現的。
var util = require("util") function Superclass(){ this.a = "a"; } Superclass.prototype.d = "d"; function Subclass(){ this.b = "b"; } util.inherits(Subclass, Superclass); var superC = new Superclass(); var subC = new Subclass(); console.log(superC.a); console.log(subC.a); subC.a = "suba"; console.log(superC.a); console.log(subC.a); subC.cc = "cc"; console.log(superC.cc); console.log(subC.cc); console.log(superC.d); console.log(subC.d);
結果:
a undefined a suba undefined cc d d Superclass { a: "a" }
subC僅僅繼承了superC在prototype中定義的屬性d,而構造函數內部創造的a屬性沒有被subC繼承。同時,在原型中定義的屬性不會被console.log作為對象的屬性輸出。在subC中修改屬性a并不會修改superC的屬性a,但是能獲取superC的屬性d,而且設置了一個屬性cc也不會影響superC。所以對于set操作并不會修改原型鏈,只有get操作才會體會到原型鏈(繼承)的存在。
var util = require("util") function Superclass(){ this.a = "a"; } Superclass.prototype.d = "d"; function Subclass(){ this.b = "b"; } util.inherits(Subclass, Superclass); var superC = new Superclass(); var subC = new Subclass(); for(x in subC){ console.log(x); }
結果是
b d
也就是說 in 關鍵字能檢測到自有屬性和繼承熟性,這個可以用 !==來代替
for(x in subC){ if(subC[x] !== undefined) console.log(x); }
結果一致, 但是in可以區分屬性不存在 和 屬性存在且為undefined 兩種情況,而!==不能區分。
再看下面的:
for(x in subC){ if(subC.hasOwnProperty(x)) console.log(x); }
結果是
b
也就是說hasOwnProperty能檢測到自身屬性,不包含繼承屬性。總結一下:
superC.hasOwnProperty(); //自有屬性為真 superC.propertyIsEnumerable(superC); //可枚舉屬性為真 Object.keys(superC); //所有可枚舉自有屬性 Object.getOwnPropertyNames(superC); //所有自有屬性 for x in superC //自有及其其原型鏈上繼承到的可枚舉屬性依然是作用域
看看這段代碼:
for(var i = 0; i < 5; i++){ setTimeout(function(){ console.log(i); },100*i) }
你可能會認為結果是
0 1 2 3 4
但是結果是
5 5 5 5 5
原因就是 settimeout的回調函數執行時,for循環已經執行完畢。i變成了5,而回調函數最近的原型作用域上的i(此處也就是全局作用域)就是5,自然就是5了。要達到想要的效果正確的做法是:
for(var i = 0; i < 5; i++){ (function(i){setTimeout(function(){ console.log(i); },100*i)})(i) }
即用 (function(i){})(i);來產生一個立即作用域,保證settimeout回調函數執行的時候最近的原型作用域的i就是當時循環的i。
說到這個就得談談閉包:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。用大白話:閉包的作用就是在一個函數執行完并返回后,并不回收該函數所占用的資源,因為該函數的內部函數(或屬性)的執行需要依賴該函數中的屬性。
function outF() { var count = 0; return function inF(){ count++; console.log(count); } } var inF= outF(); inF(); // 1 inF(); // 2
可見outF執行過后,其屬性count并未回收。回到上面那個錯誤的循環,for創建了若干個閉包,每個閉包共享上下文環境 i。因為for(很大可能)會先跑完,所以運行回調函數的時候 i 已經變成了5。而正確的循環中,也通過匿名函數創建了閉包,這個匿名函數作為外部函數,通過立即調用,使得settimeout不需要共享循環中的i,而是獨享每一次循環不同的i。
作用域真的可以說是JavaScript的一個問題,var 聲明是具有整個函數內部的可見性,而js1.7之后的let的出現算是彌補了這個缺陷(至于是不是缺陷就見仁見智了),let 聲明的變量只屬于就近的花括號內部,看下面的代碼
for(let i = 0; i < 5; i++){ setTimeout(function(){ console.log(i); },100*i) }
結果就是
0 1 2 3 4
區別就在于使用了 var 和 let 來生明變量i,let 使得每次程序進入花括號就產生了一個塊級作用域,也就是說settimeout的回調函數作用域鏈中最近的i不再是全局的i,而是塊級作用域的i,也就是每一次不同的0,1,2,3,4而不是全局i最后是5。let產生了和上面立即作用域相同的效果。
對象類型非常奇怪,在Javascript中沒有非常簡單的獲取一個對象的類別的方法,instanceof 是要檢查原型鏈的,類似于isPropertypeOf,所以無法一步到位獲得最精確地的對象類型,一般用下面這個 classof可以獲得最精確的類型
var a = new Date(); function classof(o){ if(o===null) return "Null"; if(o===undefined) return "Undefined"; return Object.prototype.toString.call(o).slice(8,-1); } console.log(classof(a));//Date
之所以不直接用Object.prototype.toString,是因為好多類型重寫了這個方法,不能保證它輸出是
[object class],所以使用Function.call方法。
nodejs中分號; 是可選的,這個有一定程度的便利,可是在我看來它更多的是造成了混亂,js會在必要的時候幫助我們添加分號,它有自己的添加規則(我們自然都懶得去記)。
var a a = 3 console.log(a)
這個會解析成
var a; a = 3;console.log(a);
沒啥毛病。
可是
var equa = function(a,b){ if(a===b){ return true; } return false; } console.log(equa(5,5));//undefined
就沒有按預期執行,因為它解析成了 return;true;返回的自然是undefined。
所以避免混亂最簡單的做法就是老老實實的給每一句都加上 ;
a = [];a[1000]=5; //a.length=1001,雖然a只有一個元素 a1 = [,,,]; // [undefined,undefined,undefined] a2 = new Array(3); //數組根本沒有元素 0 in a1;// true ,如上所說,in 可以區分元素不存在和元素值存在且為undefined的情況 0 in a2;// false高級數組方法
filter():“過濾”功能,數組中的每一項運行給定函數,返回滿足過濾條件組成的數組。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var arr2 = arr.filter(function(x, index) { return index % 3 === 0 || x >= 8; }); console.log(arr2); //[1, 4, 7, 8, 9, 10]
every():判斷數組中每一項都是否滿足條件,只有所有項都滿足條件,才會返回true。
var arr = [1, 2, 3, 4, 5]; var arr2 = arr.every(function(x) { return x < 10; }); console.log(arr2); //true var arr3 = arr.every(function(x) { return x < 3; }); console.log(arr3); // false
some():判斷數組中是否存在滿足條件的項,只要有一項滿足條件,就會返回true。
reduce() 和 reduceRight(),這兩個方法都會實現迭代數組的所有項,然后構建一個最終返回的值。reduce()方法從數組的第一項開始,逐個遍歷到最后。而 reduceRight()則從數組的最后一項開始,向前遍歷到第一項。
這兩個方法都接收兩個參數:一個在每一項上調用的函數和(可選的)作為歸并基礎的初始值。
傳給 reduce()和 reduceRight()的函數接收 4 個參數:前一個值、當前值、項的索引和數組對象。這個函數返回的任何值都會作為第一個參數自動傳給下一項。第一次迭代發生在數組的第二項上,因此第一個參數是數組的第一項,第二個參數就是數組的第二項。
下面代碼用reduce()實現數組求。
var values = [1,2,3,4,5]; var sum = values.reduceRight(function(prev, cur, index, array){ return prev + cur; },0); console.log(sum); //15調用函數之 this
調用函數有4種方式,不同之處就在于調用上下文,也就是關鍵字(不是變量,不是屬性名)this的值
函數調用 ,亦即直接調用一個函數,在非嚴格模式下this指向全局對象,嚴格模式下指向undefined。需要注意的是嵌套函數的this并不指向外層函數的上下文,而是也遵照這個規則。
方法調用 ,亦即作為一個類的方法調用一個函數,this指向這個類的對象
構造器調用 ,亦即使用 new 關鍵字,this指向新建的對象本身
call,apply調用 ,this指向該函數綁定的對象
參考JavaScript 權威指南 第六版;
JavaScript 語言精粹;
深入淺出 nodejs;
http://blog.csdn.net/u0146071...
https://developer.mozilla.org...
歡迎訪問我的主頁 Mageek`s Wonderland http://mageek.cn/archives/32/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83827.html
摘要:正式發布已正式發布,新版本重點關注工具鏈以及工具鏈在中的運行速度問題。文章內容包括什么是內存,內存生命周期,中的內存分配,內存釋放,垃圾收集,種常見的內存泄漏以及如何處理內存泄漏的技巧。 1. Angular 6 正式發布 Angular 6.0.0 已正式發布,新版本重點關注工具鏈以及工具鏈在 Angular 中的運行速度問題。Angular v6 是統一整體框架、Material ...
摘要:正式發布已正式發布,新版本重點關注工具鏈以及工具鏈在中的運行速度問題。文章內容包括什么是內存,內存生命周期,中的內存分配,內存釋放,垃圾收集,種常見的內存泄漏以及如何處理內存泄漏的技巧。 1. Angular 6 正式發布 Angular 6.0.0 已正式發布,新版本重點關注工具鏈以及工具鏈在 Angular 中的運行速度問題。Angular v6 是統一整體框架、Material ...
摘要:正式發布已正式發布,新版本重點關注工具鏈以及工具鏈在中的運行速度問題。文章內容包括什么是內存,內存生命周期,中的內存分配,內存釋放,垃圾收集,種常見的內存泄漏以及如何處理內存泄漏的技巧。 1. Angular 6 正式發布 Angular 6.0.0 已正式發布,新版本重點關注工具鏈以及工具鏈在 Angular 中的運行速度問題。Angular v6 是統一整體框架、Material ...
摘要:下面是收集了一些中的一些小技巧,會不定時更新,歡迎留言補充。專業的叫法是,可以保持唯一性,具有復雜的算法,這里僅僅介紹簡單的。以下列舉幾種生成方法第一種隨機程度可以隨著的調用次數而變化第二種原理差不多交換值第一種第二種請自行領悟。 下面是收集了一些Javascript中的一些小技巧,會不定時更新,歡迎留言補充。 數字0-6到一二三四五六日的對應 Javascript中的日期對象得到...
閱讀 1402·2021-11-22 09:34
閱讀 1378·2021-09-22 14:57
閱讀 3400·2021-09-10 10:50
閱讀 1372·2019-08-30 15:54
閱讀 3690·2019-08-29 17:02
閱讀 3472·2019-08-29 12:54
閱讀 2611·2019-08-27 10:57
閱讀 3316·2019-08-26 12:24