你不知道的JS(一)

1、RHS(Right-Hand-Side)查詢與LHS(Left-Hand-Side)查詢

“RHS 查詢與簡單地查找某個(gè)變量的值別無二致(a),而LHS 查詢則是試圖找到變量的容器本身,從而可以對(duì)其賦值(a=2)?!百x值操作的目標(biāo)是誰(LHS)”以及“誰是賦值操作的源頭(RHS)”
RHS查詢:相當(dāng)于查找某個(gè)變量的值(a),如果是RHS查詢,當(dāng)引擎發(fā)現(xiàn)沒有a變量的時(shí)候,會(huì)拋出ReferenceError異常。當(dāng)查詢到該變量時(shí),如果該查詢對(duì)變量進(jìn)行了非正確的操作(非函數(shù)類型的值進(jìn)行函數(shù)調(diào)用),就會(huì)拋出TypeError異常

LHS查詢,相當(dāng)于對(duì)某個(gè)變量進(jìn)行賦值(a=2),如果是LHS查詢,當(dāng)引擎發(fā)現(xiàn)沒有a變量時(shí),會(huì)在改作用域創(chuàng)建一個(gè)a變量(在非嚴(yán)格模式下)


2、變量查找

“作用域查找會(huì)在找到第一個(gè)匹配的標(biāo)識(shí)符時(shí)停止。在多層的嵌套作用域中可以定義同名的標(biāo)識(shí)符,這叫作“遮蔽效應(yīng)”(內(nèi)部的標(biāo)識(shí)符“遮蔽”了外部的標(biāo)識(shí)符)。拋開遮蔽效應(yīng),作用域查找始終從運(yùn)行時(shí)所處的最內(nèi)部作用域開始,逐級(jí)向外或者說向上進(jìn)行,直到遇見第一個(gè)匹配的標(biāo)識(shí)符為止。”
全局變量會(huì)成為全局對(duì)象的屬性,我們可以通過對(duì)全局對(duì)象的引用,來使用被遮蔽的全局變量。


3、欺騙詞法

通過兩種機(jī)制對(duì)詞法作用域進(jìn)行 “修改”。

1)、eval()

"eval(..) 函數(shù)可以接受一個(gè)字符串為參數(shù),并將其中的內(nèi)容視為好像在書寫時(shí)就存在于程序中這個(gè)位置的代碼"

function foo(str, a){

    eval( str ); // 欺騙!--eval將var b=3寫在了該位置,在該作用域中聲明了一個(gè)新變量b

    console.log( a, b );

}

var b = 2;

foo( "var b = 3;", 1 ); // 1, 3

JavaScript 中還有其他一些功能效果和eval(..) 很相似。setTimeout(..) 和 setInterval(..) 的第一個(gè)參數(shù)可以是字符串,字符串的內(nèi)容可以被解釋為一段動(dòng)態(tài)生成的 函數(shù)代碼。

2)、with關(guān)鍵字

with 通常被當(dāng)作重復(fù)引用同一個(gè)對(duì)象中的多個(gè)屬性的快捷方式,可以不需要重復(fù)引用對(duì)象本身。

with 可以將一個(gè)沒有或有多個(gè)屬性的對(duì)象處理為一個(gè)完全隔離的詞法作用域,因此這個(gè)對(duì) 象的屬性也會(huì)被處理為定義在這個(gè)作用域中的詞法標(biāo)識(shí)符。

var str={a="kk",b="is",c="happpy"};

str.a="ss";//平常的引用,需要重復(fù)使用str來引用

with(str){a="ss";b="isn't";c="sad"}//快捷引用

function foo(obj) {

    with (obj) {a = 2;}

}

var o1 = {a: 3};

var o2 = {b: 3};

foo( o1 );

console.log( o1.a ); // 2

foo( o2 );

console.log( o2.a ); // undefined

console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!

因?yàn)樵趏2對(duì)象中,并不存在屬性a,所以,不會(huì)創(chuàng)建a屬性,o2.a=undefined,但是因?yàn)檫M(jìn)行了LHS標(biāo)識(shí)符查找,所以就創(chuàng)建了一個(gè)全局變量a。

所以如果使用with對(duì)對(duì)象進(jìn)行屬性讀取的話,相當(dāng)于進(jìn)行了LHS標(biāo)識(shí)符查詢,當(dāng)全局都不存在變量a的情況下,會(huì)在全局的作用域創(chuàng)建一個(gè)變量a

欺騙詞法的方式對(duì)javaScript的性能會(huì)造成影響:

JavaScript 引擎會(huì)在編譯階段進(jìn)行數(shù)項(xiàng)的性能優(yōu)化。其中有些優(yōu)化依賴于能夠根據(jù)代碼的詞法進(jìn)行靜態(tài)分析,并預(yù)先確定所有變量和函數(shù)的定義位置,才能在執(zhí)行過程中快速找到標(biāo)識(shí)符。

如果引擎在代碼中發(fā)現(xiàn)了eval(..) 或with,它只能簡單地假設(shè)關(guān)于標(biāo)識(shí)符位置的判斷 都是無效的,因?yàn)闊o法在詞法分析階段明確知道eval(..) 會(huì)接收到什么代碼,這些代碼會(huì) 如何對(duì)作用域進(jìn)行修改,也無法知道傳遞給with 用來創(chuàng)建新詞法作用域的對(duì)象的內(nèi)容到底 是什么。

如果代碼中大量使用eval(..) 或with,那么運(yùn)行起來一定會(huì)變得非常慢。無論引擎多聰 明,試圖將這些悲觀情況的副作用限制在最小范圍內(nèi),也無法避免如果沒有這些優(yōu)化,代 碼會(huì)運(yùn)行得更慢這個(gè)事實(shí)。


4、作用域

javaScript具有基于函數(shù)的作用域

通過作用域,我們可以進(jìn)行隱藏內(nèi)部的實(shí)現(xiàn)

可以把變量和函數(shù)包裹在一個(gè)函數(shù)的作用域中,然后用這個(gè)作用域 來“隱藏”它們。
可以規(guī)避沖突

可以避免同名標(biāo)識(shí)符之間的沖突, 兩個(gè)標(biāo)識(shí)符可能具有相同的名字但用途卻不一樣,無意間可能造成命名沖突。沖突會(huì)導(dǎo)致 變量的值被意外覆蓋。

function foo() {

    function bar(a) {

        i = 3; // 修改for 循環(huán)所屬作用域中的i

        console.log( a + i );}

        for (var i=0; i<10; i++) { bar( i * 2 ); // 糟糕,無限循環(huán)了! }

}

foo();

函數(shù)與函數(shù)表達(dá)式

區(qū)分函數(shù)聲明和表達(dá)式最簡單的方法是看function 關(guān)鍵字出現(xiàn)在聲明中的位 置(不僅僅是一行代碼,而是整個(gè)聲明中的位置)。如果function 是聲明中 的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。

var a = 2; 

(function foo(){ // <-- 添加這一行

    var a = 3;

    console.log( a ); // 3

})(); // <-- 以及這一行

console.log( a ); // 2

使用函數(shù)表達(dá)式,可以不需要顯示的聲明foo()后還有通過函數(shù)名調(diào)用這個(gè)函數(shù)才能使其運(yùn)行。此時(shí),foo 被綁定在函數(shù)表達(dá)式自身的函數(shù)中而不是所在作用域中。換句話說,(function foo(){ .. }) 作為函數(shù)表達(dá)式意味著foo 只能在.. 所代表的位置中被訪問,外部作用域則不行。foo 變量名被隱藏在自身中意味著不會(huì)非必要地污染外部作用域。

匿名與具名

setTimeout( function() {

    console.log("I waited 1 second!");

}, 1000 );

這叫作匿名函數(shù)表達(dá)式,因?yàn)閒unction().. 沒有名稱標(biāo)識(shí)符。函數(shù)表達(dá)式可以是匿名的, 而函數(shù)聲明則不可以省略函數(shù)名——在JavaScript 的語法中這是非法的。

IIFE,代表立即執(zhí)行函數(shù)表達(dá)式 (Immediately Invoked Function Expression),第一個(gè)()將函數(shù)變成表達(dá)式,第二個(gè)()執(zhí)行了這個(gè)函數(shù)。還有另一種形式:(function(){ .. }())。用于調(diào)用的()被移進(jìn)了用來包裝的()中
匿名函數(shù)表達(dá)式簡單快捷,但由于匿名,不容易調(diào)試,在需要引用自身時(shí),只能使用已經(jīng)過期的arguments.callee引用,可讀性差

塊作用域

{

//塊作用域

}

1、用with 從對(duì)象中創(chuàng)建出的作用域僅在with 聲明中而非外 部作用域中有效。
functionfoo(obj) {

with(obj) {var a=2;console.log(a);//2}

}

varo2 ={b:3};

foo(o2);

console.log(o2.a);// undefined
2、ES3 規(guī)范中規(guī)定try/catch 的catch 分句會(huì)創(chuàng)建一個(gè)塊作 用域,其中聲明的變量僅在catch 內(nèi)部有效。
3、ES6 中,引入了新的let 關(guān)鍵字,提供了除var 以外的另一種變量聲明方式。 let 關(guān)鍵字可以將變量綁定到所在的任意作用域中(通常是{ .. } 內(nèi)部)。換句話說,let 為其聲明的變量隱式地了所在的塊作用域。使用let 進(jìn)行的聲明不會(huì)在塊作用域中進(jìn)行提升(見5)。聲明的代碼被運(yùn)行之前,聲明并不 “存在”。

由于let 聲明附屬于一個(gè)新的作用域而不是當(dāng)前的函數(shù)作用域(也不屬于全局作用域), 當(dāng)代碼中存在對(duì)于函數(shù)作用域中var 聲明的隱式依賴時(shí),就會(huì)有很多隱藏的陷阱,如果用 let 來替代var 則需要在代碼重構(gòu)的過程中付出額外的精力。

4、ES6 還引入了const,同樣可以用來創(chuàng)建塊作用域變量,但其值是固定的 (常量)。之后任何試圖修改值的操作都會(huì)引起錯(cuò)誤

5、提升

提升是指聲明會(huì)被視為存在于其所出現(xiàn)的作用域的整個(gè)范圍內(nèi)
引擎在解釋js代碼前,會(huì)先進(jìn)行編譯,編譯中的一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺麄冴P(guān)聯(lián)起來。所以var a=2中,var a是在編譯階段進(jìn)行,a=2;會(huì)被留在執(zhí)行階段。

這個(gè)過程就好像變量和函數(shù)聲明從它們?cè)诖a中出現(xiàn)的位置被“移動(dòng)” 到了最上面。這個(gè)過程就叫作提升。先有蛋(聲明)后有雞(賦值)。
函數(shù)聲明會(huì)被提升,但是函數(shù)表達(dá)式卻不會(huì)被提升

foo();

function foo() {

    console.log( a ); // undefined

    var a = 2;

}

foo(); // 不是ReferenceError, 而是TypeError!

var foo = function bar() { // ... };

函數(shù)聲明和變量聲明都會(huì)被提升。但是一個(gè)值得注意的細(xì)節(jié)(這個(gè)細(xì)節(jié)可以出現(xiàn)在有多個(gè) “重復(fù)”聲明的代碼中)是函數(shù)會(huì)首先被提升,然后才是變量。

總結(jié):引擎是先執(zhí)行 詞法作用域,所以聲明會(huì)先被運(yùn)行,其中函數(shù)聲明會(huì)優(yōu)先于變量聲明。但是let的聲明方式,是不能進(jìn)行提升的。


6、作用域閉包

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用 域之外執(zhí)行。

function foo() {

    var a = 2;

    function bar() { console.log( a ); }

    return bar;

}

var baz = foo();

baz(); // 2 —— 朋友,這就是閉包的效果。

bar()在自己的詞法作用域之外執(zhí)行。在foo()執(zhí)行之后,本該對(duì)其進(jìn)行回收(引擎的垃圾回收器來釋放不在使用的內(nèi)存空間)。但由于存在閉包,bar()本身在使用內(nèi)部作用域,所以該作用域可以一直存活。使得bar()可以在之后的任何時(shí)間進(jìn)行引用。

循環(huán)與閉包

for (var i=1; i<=5; i++) {

    setTimeout( function timer() {

        console.log( i );

    },0 );

}//6 6 6 6 6 6

五個(gè)setTimeout都被封閉在一個(gè)共享的全局作用域中,所以只有一個(gè)i。

for (var i=1; i<=5; i++) {

    (function(j) {

        setTimeout( function timer()

        { console.log( j );

        }, j*1000 );

    })( i );

}//1、2、3、4、5

在迭代內(nèi)使用IIFE 會(huì)為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的 作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)含有一個(gè)具有正確值的變量供我們?cè)L問。

使用IIFE來創(chuàng)建一個(gè)函數(shù)作用域,這樣,就會(huì)擁有五個(gè)相互獨(dú)立的作用域

for (let i=1; i<=5; i++)

{

    setTimeout( function timer() {

        console.log( i ); }, 0 );

}//1 2 3 4 5

for 循環(huán)頭部的let 聲明還會(huì)有一 個(gè)特殊的行為。這個(gè)行為指出變量在循環(huán)過程中不止被聲明一次,每次迭代都會(huì)聲明。隨 后的每個(gè)迭代都會(huì)使用上一個(gè)迭代結(jié)束時(shí)的值來初始化這個(gè)變量
通過let來建立塊級(jí)作用域,這樣,也會(huì)擁有五個(gè)相互獨(dú)立的作用域


7、模塊

function CoolModule() {
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething() {
        console.log( something );
    }
    function doAnother() {
        console.log( another.join( " ! " ) );
    }
    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

CoolModule()是一個(gè)函數(shù),必須要通過調(diào)用它來創(chuàng)建一個(gè)模塊實(shí)例。它的返回值是{key:value}的形式,通過這個(gè)形式來表示對(duì)象。在這個(gè)函數(shù)中,返回的是內(nèi)部函數(shù),而不是內(nèi)部數(shù)據(jù)變量。所以變量是隱藏且私有的狀態(tài)。doSomething()和doAnother()函數(shù)具有涵蓋模塊實(shí)例內(nèi)部作用域的閉包。

模塊模式需要具備兩個(gè)必要條件:

1、必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)
2、封閉函數(shù)必須至少返回一個(gè)內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)。

一個(gè)具有函數(shù)屬性的對(duì)象本身并不是真正的模塊。從方便觀察的角度看,一個(gè)從函數(shù)調(diào)用所返回的,只有數(shù)據(jù)屬性而沒有閉包函數(shù)的對(duì)象并不是真正的模塊。
var foo = (function CoolModule(id) {
function change() {
// 修改公共API
  publicAPI.identify = identify2;
}
function identify1() {
  console.log( id );
}
function identify2() {
  console.log( id.toUpperCase() );
}
var publicAPI = {
  change: change,
  identify: identify1
};
  return publicAPI;
})( "foo module" );
foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE

命名將要作為公共API返回的對(duì)象。通過在模塊實(shí)例的內(nèi)部保留對(duì)公共API對(duì)象的內(nèi)部引用,可以從內(nèi)部對(duì)模塊進(jìn)行修改,包括添加刪除方法、屬性,以及修改他們的值。

8、this

  • 弄清楚this指向的意義,在于知道他所指向的作用域。

this是在運(yùn)行時(shí)被綁定的,并不是在編寫時(shí),他的上下文取決于函數(shù)調(diào)用時(shí)的各種條件。this的綁定和函數(shù)聲明的位置沒有關(guān)系,只取決于函數(shù)的調(diào)用方式。

/**使用call函數(shù),使得this指向foo函數(shù)**/
function foo(num) {
console.log( "foo: " + num );
// 記錄 foo 被調(diào)用的次數(shù)
// 注意, 在當(dāng)前的調(diào)用方式下(參見下方代碼), this 確實(shí)指向 foo
this.count++;
} 
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
// 使用 call(..) 可以確保 this 指向函數(shù)對(duì)象 foo 本身
foo.call( foo, i );
}
} 
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被調(diào)用了多少次?
console.log( foo.count ); // 4
調(diào)用位置

調(diào)用位置是指函數(shù)在代碼中被調(diào)用的位置。我們需要關(guān)注的是當(dāng)前正在執(zhí)行的函數(shù)的前一個(gè)調(diào)用中。

function baz() {
// 當(dāng)前調(diào)用棧是: baz
// 因此, 當(dāng)前調(diào)用位置是全局作用域
console.log( "baz" );
bar(); // <-- bar 的調(diào)用位置
}
function bar() {
// 當(dāng)前調(diào)用棧是 baz -> bar
// 因此, 當(dāng)前調(diào)用位置在 baz 中
console.log( "bar" );
foo(); // <-- foo 的調(diào)用位置
}
function foo() {
// 當(dāng)前調(diào)用棧是 baz -> bar -> foo
// 因此, 當(dāng)前調(diào)用位置在 bar 中
console.log( "foo" );
} b
az(); // <-- baz 的調(diào)用位置

調(diào)用的綁定規(guī)則

  • 默認(rèn)綁定
    獨(dú)立函數(shù)調(diào)用
    this的默認(rèn)綁定:在代碼中,直接使用,不帶任何的修飾進(jìn)行調(diào)用。(在嚴(yán)格模式中,全局對(duì)象無法使用默認(rèn)綁定,this就會(huì)綁定到undefined)
/**this指向全局對(duì)象**/
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
  • 隱式綁定
    調(diào)用位置是否有上下文對(duì)象,是否被某個(gè)對(duì)象擁有或者包含。
/****/
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2

首先需要注意的是 foo() 的聲明方式, 及其之后是如何被當(dāng)作引用屬性添加到 obj 中的。但是無論是直接在 obj 中定義還是先定義再添加為引用屬性, 這個(gè)函數(shù)嚴(yán)格來說都不屬于
obj 對(duì)象。然而, 調(diào)用位置會(huì)使用 obj 上下文來引用函數(shù), 因此你可以說函數(shù)被調(diào)用時(shí) obj 對(duì)象“擁有” 或者“包含” 它。

對(duì)象屬性引用鏈中只有最頂層或者說最后一層會(huì)影響調(diào)用位置。 舉例來說:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
  • 隱式丟失
    被隱式綁定的函數(shù)會(huì)丟失綁定對(duì)象,會(huì)應(yīng)用默認(rèn)綁定,從而把this綁定到全局或者undefined上,取決于是否是嚴(yán)格模式。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函數(shù)別名!
var a = "oops, global"; // a 是全局對(duì)象的屬性
bar(); // "oops, global"

上面的例子,bar是obj.foo的一個(gè)引用,bar()是一個(gè)不帶任何修飾符的函數(shù)調(diào)用,所以是默認(rèn)綁定。

function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn 其實(shí)引用的是 foo
fn(); // <-- 調(diào)用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局對(duì)象的屬性
doFoo( obj.foo ); // "oops, global"

參數(shù)傳遞是一種隱式賦值。所以obj.foo會(huì)被賦值給fn()。所以又是默認(rèn)綁定。

  • 顯式綁定
    使用call(對(duì)象,參數(shù))、apply(對(duì)象,[參數(shù)])函數(shù)在某個(gè)對(duì)象里強(qiáng)制調(diào)用函數(shù)。
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
顯式綁定仍然存在丟失綁定問題,可以采取硬綁定的方式解決
  • 硬綁定
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬綁定的 bar 不可能再修改它的 this
bar.call( window ); // 2
  • 創(chuàng)建一個(gè)可以重復(fù)使用的輔助函數(shù)
function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
};
//輔助綁定函數(shù)
function bind(fn,obj){
    return function () {
        return fn.apply(obj,arguments)
    }
}
var bar = bind(foo,obj)
var a = "oops, global"; 
bar(); 

  • 在ES5中,可以采用function.prototyper.bind
function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
};

var bar = foo.bind(obj)
var a = "oops, global";
bar();

以上三種都是硬綁定的方式,還可以采用API調(diào)用的“上下文”

第三方庫的許多函數(shù), 以及 JavaScript 語言和宿主環(huán)境中許多新的內(nèi)置函數(shù), 都提供了一個(gè)可選的參數(shù), 通常被稱為“上下文”(context), 其作用和 bind(..) 一樣, 確保你的回調(diào)函數(shù)使用指定的 this。這些函數(shù)實(shí)際上就是通過 call(..) 或者 apply(..) 實(shí)現(xiàn)了顯式綁定, 這樣你可以少些一些代碼。

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
};
[1].forEach(foo,obj)
  • new綁定

構(gòu)造函數(shù)只是一些使用 new 操作符時(shí)被調(diào)用的函數(shù)。 它們并不會(huì)屬于某個(gè)類, 也不會(huì)實(shí)例化一個(gè)類。 實(shí)際上,它們甚至都不能說是一種特殊的函數(shù)類型, 它們只是被 new 操作符調(diào)用的普通函數(shù)而已。所以, 包括內(nèi)置對(duì)象函數(shù)(比如 Number(..), 詳情請(qǐng)查看第 3 章) 在內(nèi)的所有函數(shù)都可以用 new 來調(diào)用, 這種函數(shù)調(diào)用被稱為構(gòu)造函數(shù)調(diào)用。

使用new進(jìn)行函數(shù)調(diào)用的時(shí)候,會(huì)發(fā)生以下操作

  1. 創(chuàng)建(或者說構(gòu)造) 一個(gè)全新的對(duì)象。
  2. 這個(gè)新對(duì)象會(huì)被執(zhí)行 [[ 原型 ]] 連接。
  3. 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的 this。
  4. 如果函數(shù)沒有返回其他對(duì)象, 那么 new 表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

優(yōu)先級(jí)

  • 顯示綁定>隱式綁定
function foo() {
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
  • new 綁定>隱式綁定
function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj1.a ); // 2
obj1.foo.call( obj2, 3 );
console.log( obj2.a ); // 3
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 讀完《你不知道的js》已經(jīng)過去很久了,正好順著春招的氣息,整理一波,復(fù)習(xí)一下JS基礎(chǔ),不得不說這本書刷新了我對(duì)JS...
    SmailEvery閱讀 355評(píng)論 0 0
  • 作用域是什么 1.1編譯原理 JavaScript引擎編譯的步驟與傳統(tǒng)的編譯語言類似。程序中的一段源代碼在執(zhí)行前會(huì)...
    陳老板_閱讀 4,194評(píng)論 0 1
  • 大部分接觸js應(yīng)該都是先用了再學(xué),其實(shí)大部分學(xué)習(xí)大部分語言都應(yīng)該采取這種方式。因?yàn)橹豢床痪?,還能入門,而如果先看的...
    隕石墜滅閱讀 354評(píng)論 0 0
  • 前言 儲(chǔ)存和訪問變量的值,這種能力將狀態(tài)帶給了程序需要討論的問題:變量儲(chǔ)存在哪里?程序需要時(shí)如何找到它們? 202...
    林不羈吖閱讀 290評(píng)論 0 0
  • 作用域是什么 理解作用域 引擎從頭到尾負(fù)責(zé)整個(gè)JavaScript程序的編譯和執(zhí)行過程 編譯器負(fù)責(zé)語法分析及代碼生...
    devZhang閱讀 352評(píng)論 0 0

友情鏈接更多精彩內(nèi)容