摘要:命名函數(shù)的賦值表達(dá)式另外一個(gè)特殊的情況是將命名函數(shù)賦值給一個(gè)變量。這是由于的命名處理所致,函數(shù)名在函數(shù)內(nèi)總是可見的。當(dāng)需要向回調(diào)函數(shù)傳遞參數(shù)時(shí),可以創(chuàng)建一個(gè)匿名函數(shù),在函數(shù)內(nèi)執(zhí)行真實(shí)的回調(diào)函數(shù)。
1.hasOwnProperty相關(guān)
為了判斷一個(gè)對(duì)象是否包含自定義屬性而不是原型鏈上的屬性,我們需要使用繼承自 Object.prototype 的 hasOwnProperty方法。
hasOwnProperty 是 JavaScript 中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)。
</>復(fù)制代碼
// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
"bar" in foo; // true
foo.hasOwnProperty("bar"); // false
foo.hasOwnProperty("goo"); // true
</>復(fù)制代碼
注意: 通過判斷一個(gè)屬性是否 undefined 是不夠的。 因?yàn)橐粋€(gè)屬性可能確實(shí)存在,只不過它的值被設(shè)置為 undefined。
hasOwnProperty 作為屬性
JavaScript 不會(huì)保護(hù) hasOwnProperty 被非法占用,因此如果一個(gè)對(duì)象碰巧存在這個(gè)屬性, 就需要使用外部的 hasOwnProperty 函數(shù)來獲取正確的結(jié)果。
</>復(fù)制代碼
var foo = {
hasOwnProperty: function() {
return false;
},
bar: "Here be dragons"
};
foo.hasOwnProperty("bar"); // 總是返回 false
// 使用其它對(duì)象的 hasOwnProperty,并將其上下文設(shè)置為foo
({}).hasOwnProperty.call(foo, "bar"); // true
當(dāng)檢查對(duì)象上某個(gè)屬性是否存在時(shí),hasOwnProperty 是唯一可用的方法。 同時(shí)在使用 for in loop遍歷對(duì)象時(shí),推薦總是使用 hasOwnProperty 方法, 這將會(huì)避免原型對(duì)象擴(kuò)展帶來的干擾。
for in 循環(huán)和 in 操作符一樣,for in 循環(huán)同樣在查找對(duì)象屬性時(shí)遍歷原型鏈上的所有屬性。
</>復(fù)制代碼
// 修改 Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 輸出兩個(gè)屬性:bar 和 moo
}
</>復(fù)制代碼
注意: 由于 for in 總是要遍歷整個(gè)原型鏈,因此如果一個(gè)對(duì)象的繼承層次太深的話會(huì)影響性能。
由于不可能改變 for in 自身的行為,因此有必要過濾出那些不希望出現(xiàn)在循環(huán)體中的屬性, 這可以通過 Object.prototype 原型上的 hasOwnProperty 函數(shù)來完成。
使用 hasOwnProperty 過濾</>復(fù)制代碼
// foo 變量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
2.命名函數(shù)的賦值表達(dá)式</>復(fù)制代碼
推薦總是使用 hasOwnProperty。不要對(duì)代碼運(yùn)行的環(huán)境做任何假設(shè),不要假設(shè)原生對(duì)象是否已經(jīng)被擴(kuò)展了。
另外一個(gè)特殊的情況是將命名函數(shù)賦值給一個(gè)變量。
</>復(fù)制代碼
var foo = function bar() {
bar(); // 正常運(yùn)行
}
bar(); // 出錯(cuò):ReferenceError
bar 函數(shù)聲明外是不可見的,這是因?yàn)槲覀円呀?jīng)把函數(shù)賦值給了 foo; 然而在 bar 內(nèi)部依然可見。這是由于 JavaScript 的命名處理所致, 函數(shù)名在函數(shù)內(nèi)總是可見的。
</>復(fù)制代碼
注意:在IE8及IE8以下版本瀏覽器bar在外部也是可見的,是因?yàn)闉g覽器對(duì)命名函數(shù)賦值表達(dá)式進(jìn)行了錯(cuò)誤的解析, 解析成兩個(gè)函數(shù) foo 和 bar
3.方法的賦值表達(dá)式
另一個(gè)看起來奇怪的地方是函數(shù)別名,也就是將一個(gè)方法賦值給一個(gè)變量。
</>復(fù)制代碼
var test = someObject.methodTest;
test();
上例中,test 就像一個(gè)普通的函數(shù)被調(diào)用;因此,函數(shù)內(nèi)的 this 將不再被指向到 someObject 對(duì)象。而是指向了window。
4.循環(huán)中的閉包一個(gè)常見的錯(cuò)誤出現(xiàn)在循環(huán)中使用閉包,假設(shè)我們需要在每次循環(huán)中調(diào)用循環(huán)序號(hào)
</>復(fù)制代碼
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
上面的代碼不會(huì)輸出數(shù)字 0到 9,而是會(huì)輸出數(shù)字10 十次。
當(dāng) console.log 被調(diào)用的時(shí)候,匿名函數(shù)保持對(duì)外部變量i的引用,此時(shí) for循環(huán)已經(jīng)結(jié)束,i的值被修改成了10.
為了得到想要的結(jié)果,需要在每次循環(huán)中創(chuàng)建變量 i的拷貝。
為了避免引用錯(cuò)誤,為了正確的獲得循環(huán)序號(hào),最好使用 匿名包裝器(注:其實(shí)就是我們通常說的自執(zhí)行匿名函數(shù))。
</>復(fù)制代碼
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
外部的匿名函數(shù)會(huì)立即執(zhí)行,并把 i 作為它的參數(shù),此時(shí)函數(shù)內(nèi) e 變量就擁有了 i 的一個(gè)拷貝。
當(dāng)傳遞給 setTimeout 的匿名函數(shù)執(zhí)行時(shí),它就擁有了對(duì) e 的引用,而這個(gè)值是不會(huì)被循環(huán)改變的。
有另一個(gè)方法完成同樣的工作,那就是從匿名包裝器中返回一個(gè)函數(shù)。這和上面的代碼效果一樣。
</>復(fù)制代碼
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
5.對(duì)象使用和屬性
JavaScript 中所有變量都可以當(dāng)作對(duì)象使用,除了兩個(gè)例外 null 和 undefined。
</>復(fù)制代碼
false.toString(); // "false"
[1, 2, 3].toString(); // "1,2,3"
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1
一個(gè)常見的誤解是數(shù)字的字面值(literal)不能當(dāng)作對(duì)象使用。這是因?yàn)?JavaScript 解析器的一個(gè)錯(cuò)誤, 它試圖將點(diǎn)操作符解析為浮點(diǎn)數(shù)字面值的一部分。
</>復(fù)制代碼
2.toString(); // 出錯(cuò):SyntaxError
有很多變通方法可以讓數(shù)字的字面值看起來像對(duì)象。
</>復(fù)制代碼
2..toString(); // 第二個(gè)點(diǎn)號(hào)可以正常解析
2 .toString(); // 注意點(diǎn)號(hào)前面的空格
(2).toString(); // 2先被計(jì)算
刪除屬性的唯一方法是使用 delete 操作符;設(shè)置屬性為 undefined 或者 null 并不能真正的刪除屬性, 而僅僅是移除了屬性和值的關(guān)聯(lián)。
</>復(fù)制代碼
var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, "" + obj[i]);
}
}
上面的輸出結(jié)果有 bar undefined 和 foo null - 只有 baz 被真正的刪除了,所以從輸出結(jié)果中消失。
6.arguments 對(duì)象JavaScript 中每個(gè)函數(shù)內(nèi)都能訪問一個(gè)特別變量 arguments。這個(gè)變量維護(hù)著所有傳遞到這個(gè)函數(shù)中的參數(shù)列表。
arguments 變量不是一個(gè)數(shù)組(Array)。 盡管在語(yǔ)法上它有數(shù)組相關(guān)的屬性 length,但它不從 Array.prototype 繼承,實(shí)際上它是一個(gè)對(duì)象(Object)。
因此,無法對(duì) arguments 變量使用標(biāo)準(zhǔn)的數(shù)組方法,比如 push, pop 或者 slice。 雖然使用 for 循環(huán)遍歷也是可以的,但是為了更好的使用數(shù)組方法,最好把它轉(zhuǎn)化為一個(gè)真正的數(shù)組。
轉(zhuǎn)化為數(shù)組下面的代碼將會(huì)創(chuàng)建一個(gè)新的數(shù)組,包含所有 arguments 對(duì)象中的元素。
</>復(fù)制代碼
Array.prototype.slice.call(arguments);
arguments 對(duì)象為其內(nèi)部屬性以及函數(shù)形式參數(shù)創(chuàng)建 getter 和 setter 方法。
因此,改變形參的值會(huì)影響到 arguments 對(duì)象的值,反之亦然。
</>復(fù)制代碼
function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3);
如下一個(gè)例子:
</>復(fù)制代碼
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c) {
c = 10
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1)
這里所有的更改都將生效,a和c的值都為10,a+b+c的值將為21。
7.類型相關(guān) 測(cè)試為定義變量</>復(fù)制代碼
typeof foo !== "undefined"
上面代碼會(huì)檢測(cè) foo 是否已經(jīng)定義;如果沒有定義而直接使用會(huì)導(dǎo)致 ReferenceError 的異常。 這是 typeof 唯一有用的地方。當(dāng)然也能判斷出來基本類型。
Object.prototype.toString檢測(cè)一個(gè)對(duì)象的類型為了檢測(cè)一個(gè)對(duì)象的類型,強(qiáng)烈推薦使用 Object.prototype.toString 方法
如下例子:
</>復(fù)制代碼
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
類型轉(zhuǎn)換
內(nèi)置類型(比如 Number 和 String)的構(gòu)造函數(shù)在被調(diào)用時(shí),使用或者不使用 new 的結(jié)果完全不同。
</>復(fù)制代碼
new Number(10) === 10; // False, 對(duì)象與數(shù)字的比較
Number(10) === 10; // True, 數(shù)字與數(shù)字的比較
new Number(10) + 0 === 10; // True, 由于隱式的類型轉(zhuǎn)換
轉(zhuǎn)換為字符串
</>復(fù)制代碼
"" + 10 === "10"; // true
將一個(gè)值加上空字符串可以輕松轉(zhuǎn)換為字符串類型。
轉(zhuǎn)換為數(shù)字
</>復(fù)制代碼
+"10" === 10; // true
使用一元的加號(hào)操作符,可以把字符串轉(zhuǎn)換為數(shù)字。
轉(zhuǎn)換為布爾型
通過使用 否 操作符兩次,可以把一個(gè)值轉(zhuǎn)換為布爾型。
</>復(fù)制代碼
!!"foo"; // true
!!""; // false
!!"0"; // true
!!"1"; // true
!!"-1" // true
!!{}; // true
!!true; // true
8.為什么不要使用 eval
eval 函數(shù)會(huì)在當(dāng)前作用域中執(zhí)行一段 JavaScript 代碼字符串。
</>復(fù)制代碼
var foo = 1;
function test() {
var foo = 2;
eval("foo = 3");
return foo;
}
test(); // 3
foo; // 1
但是 eval 只在被直接調(diào)用并且調(diào)用函數(shù)就是 eval 本身時(shí),才在當(dāng)前作用域中執(zhí)行。
</>復(fù)制代碼
var foo = 1;
function test() {
var foo = 2;
var bar = eval;
bar("foo = 3");
return foo;
}
test(); // 2
foo; // 3
上面的代碼等價(jià)于在全局作用域中調(diào)用 eval,和下面兩種寫法效果一樣:
</>復(fù)制代碼
// 寫法一:直接調(diào)用全局作用域下的 foo 變量
var foo = 1;
function test() {
var foo = 2;
window.foo = 3;
return foo;
}
test(); // 2
foo; // 3
// 寫法二:使用 call 函數(shù)修改 eval 執(zhí)行的上下文為全局作用域
var foo = 1;
function test() {
var foo = 2;
eval.call(window, "foo = 3");
return foo;
}
test(); // 2
foo; // 3
在任何情況下我們都應(yīng)該避免使用 eval 函數(shù)。99.9% 使用 eval 的場(chǎng)景都有不使用 eval 的解決方案。
eval 也存在安全問題,因?yàn)樗鼤?huì)執(zhí)行任意傳給它的代碼, 在代碼字符串未知或者是來自一個(gè)不信任的源時(shí),絕對(duì)不要使用 eval 函數(shù)。
9.定時(shí)器 手工清空定時(shí)器</>復(fù)制代碼
var id = setTimeout(foo, 1000);
clearTimeout(id);
清除所有定時(shí)器
由于沒有內(nèi)置的清除所有定時(shí)器的方法,可以采用一種暴力的方式來達(dá)到這一目的。
</>復(fù)制代碼
// 清空"所有"的定時(shí)器
for(var i = 1; i < 1000; i++) {
clearTimeout(i);
}
可能還有些定時(shí)器不會(huì)在上面代碼中被清除(注:如果定時(shí)器調(diào)用時(shí)返回的 ID 值大于 1000), 因此我們可以事先保存所有的定時(shí)器 ID,然后一把清除。
建議不要在調(diào)用定時(shí)器函數(shù)時(shí),為了向回調(diào)函數(shù)傳遞參數(shù)而使用字符串的形式。
</>復(fù)制代碼
function foo(a, b, c) {}
// 不要這樣做
setTimeout("foo(1,2, 3)", 1000)
// 可以使用匿名函數(shù)完成相同功能
setTimeout(function() {
foo(1, 2, 3);
}, 1000)
</>復(fù)制代碼
絕對(duì)不要使用字符串作為 setTimeout 或者 setInterval 的第一個(gè)參數(shù), 這么寫的代碼明顯質(zhì)量很差。當(dāng)需要向回調(diào)函數(shù)傳遞參數(shù)時(shí),可以創(chuàng)建一個(gè)匿名函數(shù),在函數(shù)內(nèi)執(zhí)行真實(shí)的回調(diào)函數(shù)。另外,應(yīng)該避免使用 setInterval,因?yàn)樗亩〞r(shí)執(zhí)行不會(huì)被 JavaScript 阻塞。
后續(xù)逐漸添加
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/92157.html
摘要:筆記說明重學(xué)前端是程劭非前手機(jī)淘寶前端負(fù)責(zé)人在極客時(shí)間開的一個(gè)專欄,每天分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以加入的專欄學(xué)習(xí)原文有的語(yǔ)音,如有侵權(quán)請(qǐng)聯(lián)系我,郵箱。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以...
摘要:筆記說明重學(xué)前端是程劭非前手機(jī)淘寶前端負(fù)責(zé)人在極客時(shí)間開的一個(gè)專欄,每天分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以加入的專欄學(xué)習(xí)原文有的語(yǔ)音,如有侵權(quán)請(qǐng)聯(lián)系我,郵箱。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以...
摘要:筆記說明重學(xué)前端是程劭非前手機(jī)淘寶前端負(fù)責(zé)人在極客時(shí)間開的一個(gè)專欄,每天分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以加入的專欄學(xué)習(xí)原文有的語(yǔ)音,如有侵權(quán)請(qǐng)聯(lián)系我,郵箱。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完整的可以...
閱讀 837·2023-04-25 22:13
閱讀 2349·2019-08-30 15:56
閱讀 2230·2019-08-30 11:21
閱讀 659·2019-08-30 11:13
閱讀 2024·2019-08-26 14:06
閱讀 1963·2019-08-26 12:11
閱讀 2295·2019-08-23 16:55
閱讀 543·2019-08-23 15:30