您好,登錄后才能下訂單哦!
一、作用域
js中作用域是指可訪問變量,對象,函數的集合,也就是調用它們能生效的代碼區塊。在js中沒有塊級作用域,只有全局作用域和函數作用域
1、全局,函數作用域
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函數中c沒使用var聲明,所以c為全局變量,b為局部變量,綁定在f1函數下,外部訪問不到。
2、模仿塊級作用域
沒有塊級作用域,但是有if(),for()等塊語句,在塊語句內部定義的變量會保留在它們已經存在的作用域內,舉個栗子:
if(true) {
var word = 'hello';
console.log(word); //hello
}
console.log(word); //hello
if()語句存在全局作用域下,所以內部定義的變量存在于全局作用域中,無論在哪都可以訪問。
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函數中,內部定義的變量存在于add函數的作用域中,只有在add函數和塊語句中才可以訪問到,外部無法訪問。
3、使用自執行的匿名函數包裹塊語句構建塊作用域,也叫私有作用域
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()循環中訪問到,在add函數和外部都無法訪問,并且在匿名函數中定義的任何變量都會在執行結束時被銷毀,所以變量i只能在for()循環中使用。
二、執行上下文
javascript運行的代碼環境有三種:
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();
//結果:
//f3 context
//f2 context
//f1 context
全局上下文:擁有f1()
f1()的執行上下文:有變量f1Context和f2()
f2()的執行上下文:有變量f2Context和f3()
f3()的執行上下文:有變量f3Context
ECS:執行環境棧,可以理解為代碼執行的土壤,即代碼執行的地方
js是單線程,任務都為同步任務的情況下某一時間只能執行一個任務
執行一段代碼首先會進入全局上下文中,并將其壓入ECS中棧頂
首先執行f1(),為其創建執行上下文,進入到棧頂位置,全局上下文被往下壓
f1()中有f2(),再為f2()創建f2()的執行上下文,f2()進入到棧頂位置,f1()被往下壓,
依次,最終全局上下文被壓入到棧底,f3()的執行上下文在棧頂
f3()執行完后,ECS就會彈出其執行上下文(內部變量隨之被銷毀),f3()上下文彈出后,f2()上下文來到棧頂,開始執行f2(),依次,最后ECS中只剩下全局上下文,它等到應用程序退出,例如瀏覽器關閉時銷毀。
function foo(i) {
if(i == 3) {
return;
}
foo(i+1);
console.log(i);
}
foo(0);
ECS棧頂為foo(3)的的上下文,直接return彈出后,棧頂變成foo(2)的上下文,執行foo(2),輸出2并彈出,執行foo(1),輸出1并彈出,執行foo(0),輸出0并彈出,關閉瀏覽器后全局EC彈出,所以結果為2,1,0。
三、原型和原型鏈
1、對象(普通對象、函數對象)
2、構造函數
//創建構造函數
function Word(words){
this.words = words;
}
Word.prototype = {
alert(){
alert(this.words);
}
}
//創建實例
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實例本身具有的方法,所以w.print()打印hello world;alert()不屬于w實例的方法,屬于構造函數的方法,w.alert()也會打印hello world,因為實例繼承,構造函數原型上的方法。
實例w的隱式原型指向它構造函數的顯式原型,指向的意思是恒等于
w.proto === Word.prototype
當調用某種方法或查找某種屬性時,首先會在自身調用和查找,如果自身并沒有該屬性或方法,則會去它的proto屬性中調用查找,也就是它構造函數的prototype中調用查找。所以很好理解實例繼承構造函數的方法和屬性:
w本身沒有alert()方法,所以會去Word()的顯式原型Word.prototype中調用alert(),即實例繼承構造函數的方法。 ?
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
實例p上面并沒有a屬性,那么會通過proto向上查找,根據:
p.proto == Person.prototype,然后Person.prototype上也沒有a屬性,Person.prototype仍然是一個對象,上面仍然具有proto屬性,根據:
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",
因為沒有定義Object.prototype.a,只定義了Function.prototype.a
總結:
1.查找屬性,如果本身沒有,則會去proto中查找,也就是構造函數的顯式原型中查找,如果構造函數中也沒有該屬性,因為構造函數也是對象,也有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
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。