溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

javascript作用域、執(zhí)行上下文、原型和原型鏈

發(fā)布時間:2020-07-29 19:22:08 來源:網(wǎng)絡(luò) 閱讀:495 作者:xxxpjgl 欄目:web開發(fā)

一、作用域
js中作用域是指可訪問變量,對象,函數(shù)的集合,也就是調(diào)用它們能生效的代碼區(qū)塊。在js中沒有塊級作用域,只有全局作用域和函數(shù)作用域

1、全局,函數(shù)作用域

var a = 10
function f1(){
        var b = c = 20;
        console.log(a); ? ? //10
        console.log(c); ? ? //20
        function f2() {
             console.log(b); //20
        }f2();
}
f1();
console.log(a); ? ? //10
console.log(c); ? ? //20
console.log(b); ? ? //error

var b = c = 20 是指 var b = c; c = 20
在f1函數(shù)中c沒使用var聲明,所以c為全局變量,b為局部變量,綁定在f1函數(shù)下,外部訪問不到。

2、模仿塊級作用域
沒有塊級作用域,但是有if(),for()等塊語句,在塊語句內(nèi)部定義的變量會保留在它們已經(jīng)存在的作用域內(nèi),舉個栗子:

if(true) {
        var word = 'hello';
        console.log(word);  //hello
}
console.log(word);      //hello

if()語句存在全局作用域下,所以內(nèi)部定義的變量存在于全局作用域中,無論在哪都可以訪問。

function add(num) {
        if(num > 10) {
                var num = 10;
                console.log(num);   //10
        }
        console.log(num);       //10
}
add(11);
console.log(num);   //Uncaught ReferenceError: num is not defined

此時if()在add函數(shù)中,內(nèi)部定義的變量存在于add函數(shù)的作用域中,只有在add函數(shù)和塊語句中才可以訪問到,外部無法訪問。

3、使用自執(zhí)行的匿名函數(shù)包裹塊語句構(gòu)建塊作用域,也叫私有作用域

function add(num) {
        for(var i = 0; i < num; i++) {
            console.log(i);     //0,1,2,3,4,5,6,7,8,9
        }
        console.log(i);     //10
    }
add(10);

將代碼改為

function add(num) {
        (function () {
            for(var i = 0; i < num; i++) {
                     console.log(i);    //0,1,2,3,4,5,6,7,8,9
                }
        })()
    console.log(i);     //Uncaught ReferenceError: i is not defined
}
add(10);

此時變量i只能在for()循環(huán)中訪問到,在add函數(shù)和外部都無法訪問,并且在匿名函數(shù)中定義的任何變量都會在執(zhí)行結(jié)束時被銷毀,所以變量i只能在for()循環(huán)中使用。

二、執(zhí)行上下文
javascript運(yùn)行的代碼環(huán)境有三種:

  • 全局代碼:代碼默認(rèn)運(yùn)行的環(huán)境,最先會進(jìn)入到全局環(huán)境中
  • 函數(shù)代碼:在函數(shù)的局部環(huán)境中運(yùn)行的代碼
  • Eval代碼:在Eval()函數(shù)中運(yùn)行的代碼
  • 全局上下文:是最外圍的一個執(zhí)行環(huán)境,web瀏覽器中被認(rèn)為是window對象。在初始化代碼時會,先進(jìn)入全局上下文中
  • 執(zhí)行上下文:每當(dāng)一個函數(shù)被調(diào)用時就會為該函數(shù)創(chuàng)建一個執(zhí)行上下文,每個函數(shù)都有自己的執(zhí)行上下文
function f1() {
        var f1Context = 'f1 context';
        function f2() {
                var f2Context = 'f2 context';
                function f3() {
                        var f3Context = 'f3 context';
                        console.log(f3Context);
                }
                f3();
                console.log(f2Context);
        }
        f2();
        console.log(f1Context);
}
f1();
//結(jié)果:
//f3 context
//f2 context
//f1 context

全局上下文:擁有f1()
f1()的執(zhí)行上下文:有變量f1Context和f2()
f2()的執(zhí)行上下文:有變量f2Context和f3()
f3()的執(zhí)行上下文:有變量f3Context

ECS:執(zhí)行環(huán)境棧,可以理解為代碼執(zhí)行的土壤,即代碼執(zhí)行的地方
js是單線程,任務(wù)都為同步任務(wù)的情況下某一時間只能執(zhí)行一個任務(wù)

執(zhí)行一段代碼首先會進(jìn)入全局上下文中,并將其壓入ECS中棧頂
首先執(zhí)行f1(),為其創(chuàng)建執(zhí)行上下文,進(jìn)入到棧頂位置,全局上下文被往下壓
f1()中有f2(),再為f2()創(chuàng)建f2()的執(zhí)行上下文,f2()進(jìn)入到棧頂位置,f1()被往下壓,
依次,最終全局上下文被壓入到棧底,f3()的執(zhí)行上下文在棧頂
f3()執(zhí)行完后,ECS就會彈出其執(zhí)行上下文(內(nèi)部變量隨之被銷毀),f3()上下文彈出后,f2()上下文來到棧頂,開始執(zhí)行f2(),依次,最后ECS中只剩下全局上下文,它等到應(yīng)用程序退出,例如瀏覽器關(guān)閉時銷毀。
javascript作用域、執(zhí)行上下文、原型和原型鏈

function foo(i) {
        if(i  == 3) {
                return;
        }
        foo(i+1);
        console.log(i);
}
foo(0);

ECS棧頂為foo(3)的的上下文,直接return彈出后,棧頂變成foo(2)的上下文,執(zhí)行foo(2),輸出2并彈出,執(zhí)行foo(1),輸出1并彈出,執(zhí)行foo(0),輸出0并彈出,關(guān)閉瀏覽器后全局EC彈出,所以結(jié)果為2,1,0。

三、原型和原型鏈
1、對象(普通對象、函數(shù)對象)

  • 所有引用類型(函數(shù),數(shù)組,對象)都擁有proto屬性(隱式原型)
  • 所有函數(shù)擁有prototype屬性(顯式原型)(僅限函數(shù))
  • 原型對象:擁有prototype屬性的對象,在定義函數(shù)時就被創(chuàng)建

2、構(gòu)造函數(shù)
//創(chuàng)建構(gòu)造函數(shù)

function Word(words){
        this.words = words;
}
Word.prototype = {
        alert(){
                alert(this.words);
        }
}
//創(chuàng)建實(shí)例
var w = new Word("hello world");
w.print = function(){
        console.log(this.words);
        console.log(this);  //Person對象
}
w.print();  //hello world
w.alert();  //hello world

print()方法是w實(shí)例本身具有的方法,所以w.print()打印hello world;alert()不屬于w實(shí)例的方法,屬于構(gòu)造函數(shù)的方法,w.alert()也會打印hello world,因?yàn)閷?shí)例繼承,構(gòu)造函數(shù)原型上的方法。
實(shí)例w的隱式原型指向它構(gòu)造函數(shù)的顯式原型,指向的意思是恒等于
w.proto === Word.prototype

當(dāng)調(diào)用某種方法或查找某種屬性時,首先會在自身調(diào)用和查找,如果自身并沒有該屬性或方法,則會去它的proto屬性中調(diào)用查找,也就是它構(gòu)造函數(shù)的prototype中調(diào)用查找。所以很好理解實(shí)例繼承構(gòu)造函數(shù)的方法和屬性:
w本身沒有alert()方法,所以會去Word()的顯式原型Word.prototype中調(diào)用alert(),即實(shí)例繼承構(gòu)造函數(shù)的方法。 ?

3、原型和原型鏈

Function.prototype.a = "a";
Object.prototype.b = "b";
function Person(){}
console.log(Person);    //function Person()
let p = new Person();
console.log(p);         //Person {} 對象
console.log(p.a);       //undefined
console.log(p.b);       //b

實(shí)例p上面并沒有a屬性,那么會通過proto向上查找,根據(jù):
p.proto == Person.prototype,然后Person.prototype上也沒有a屬性,Person.prototype仍然是一個對象,上面仍然具有proto屬性,根據(jù):
Person.prototype.proto == Function.prototype //false

Person.prototype.proto == Object.prototype //true

Object.prototype.proto == null //再上一級就是null了

此時,Object.prototype.b = "b",所以p.a是undefined,而p.b是"b",
因?yàn)闆]有定義Object.prototype.a,只定義了Function.prototype.a

總結(jié):

1.查找屬性,如果本身沒有,則會去proto中查找,也就是構(gòu)造函數(shù)的顯式原型中查找,如果構(gòu)造函數(shù)中也沒有該屬性,因?yàn)闃?gòu)造函數(shù)也是對象,也有proto,那么會去它的顯式原型中查找,一直到null,如果沒有則返回undefined
2.p.proto.constructor? == function Person(){}
3.p._proto.proto_== Object.prototype
4.p.
proto.proto.proto== Object.prototype.proto == null ????????
5.通過
proto__形成原型鏈而非protrotype

javascript作用域、執(zhí)行上下文、原型和原型鏈

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI