原型與原型鏈
說到原型,就不得不提一下建構函式,首先我們看下面乙個簡單的例子:
function dog(name,age)
let dog1 = new dog("哈士奇",3);
let dog2 = new dog("泰迪",2);
首先創造空的物件,再讓 this 指向這個物件,通過 this.name 進行賦值,最終返回 this,這其實也是 new 乙個物件的過程。
其實: let obj = {} 是 let obj = new object() 的語法糖; let arr = 是 let arr = new array() 的語法糖; function dog() 是 let dog = new fucntion() 的語法糖。
那什麼是原型那?
在 js 中,所有物件都是 object 的例項,並繼承 object.prototype 的屬性和方法,但是有一些是隱性的。
我們來看一下原型的規則:
所有的引用型別(包括陣列,物件,函式)都具有物件特性;可自由擴充套件屬性。
var obj = {};
obj.attribute = "三座大山";
var arr = ;
arr.attribute = "三座大山";
function fn1 () {}
fn1.attribute = "三座大山";
所有的引用型別(包括數,物件,函式)都有隱性原型屬性(proto), 值也是乙個普通的物件。
nsole.log(obj.__proto__);
所有的函式,都有乙個 prototype 屬性,值也是乙個普通的物件。
console.log(obj.prototype);
所有的引用型別的proto屬性值都指向建構函式的 prototype 屬性值。
console.log(obj.__proto__ === object.prototype); // true
當試圖獲取物件屬性時,如果物件本身沒有這個屬性,那就會去他的proto(prototype)中去尋找。
function dog(name)
dog.prototype.callname = function ()
let dog1 = new dog("three mountain");
dog1.printname = function ()
dog1.callname(); // three mountain wang wang
dog1.printname(); // three mountain
原型鏈:如下圖。找乙個屬性,首先會在 f.proto中去找,因為屬性值為乙個物件,那麼就會去 f.proto.proto去找,同理如果還沒找到,就會一直向上去查詢,直到結果為 null 為止。這個串起來的鏈即為原型鏈。
作用域及閉包
講到作用域,你會想到什麼?當然是執行上下文。每個函式都有自己的 excution context,和 variable object。這些環境用於儲存上下文中的變數,函式宣告,引數等。只有函式才能製造作用域。
ps:for if else 不能創造作用域。
console.log(a) ; // undefined
var a = 1;
//可理解為
var a;
console.log(a); // undefined
a = 1;
執行 console.log 時,a 只是被宣告出來,並沒有賦值;所以結果當然是 undefined。
this
本質上來說,在 js 裡 this 是乙個指向函式執行環境的指標。this 永遠指向最後呼叫它的物件,並且在執行時才能獲取值,定義是無法確認他的值。
var a =
} a.fn() // this === a
a 呼叫了fn() 所以此時this為a
a.fn.call () // this ===
使用call(),將this的值指定為
var fn1 = a.fn
fn1() // this === window
雖然指定fn1 = a.fn,但是呼叫是有window呼叫,所以this 為window
this 有多種使用場景,下面我會主要介紹 4 個使用場景:
作為建構函式執行
function student(name,age)
var s = new student("py1988",30)
function fn ()
fn ()
作為物件屬性執行
var obj =
}obj.printname ()
三個函式可以修改 this 的指向,具體請往下看:
var name = "小明" , age = "17"
var obj =
} console.log(obj.objage) // 17
obj.fun() // 安妮今年undefined
var name = "小明" , age = "17"
var obj =
} var a =
obj.fun.call(a,"蘋果","香蕉") // jay今年23 喜歡吃蘋果不喜歡吃香蕉
obj.fun.bind(a,"蘋果","香蕉")() // jay今年23 喜歡吃蘋果不喜歡吃香蕉
閉包
閉包的概念很抽象,看下面的例子你就會理解什麼叫閉包了:
function a();
}var c = new a();
c.fun(); //1
c.fun(); //2
閉包就是能夠讀取其他函式內部變數的函式。在 js 中只有函式內部的子函式才能讀取區域性變數。所以可以簡單的理解為:定義在內部函式的函式。
用途主要有兩個:
非同步和單執行緒
我們先感受下非同步。
console.log("start");
settimeout(function () , 1000);
console.log("end");
使用非同步後,列印的順序為 start-> end->medium。因為沒有阻塞。
為什麼會產生非同步呢?
首先因為 js 為單執行緒,也就是說 cpu 同一時間只能處理乙個事務。得按順序,乙個乙個處理。
如上例所示,第一步:執行第一行列印 「start」;第二步:執行 settimeout,將其中的函式分存起來,等待時間結束後執行;第三步:執行最後一行,列印 「end」;第四部:處於空閒狀態,檢視暫存中,是否有可執行的函式;第五步:執行分存函式。
為什麼 js 引擎是單執行緒?
js 的主要用途是與使用者互動,以及操作 dom,這決定它只能是單執行緒。例:乙個執行緒要新增 dom 節點,乙個執行緒要刪減 dom 節點,容易造成分歧。
為了更好使用多 cpu,h5 提供了 web worker 標準,允許 js 建立多執行緒,但是子執行緒受到主線程控制,而且不得操作 dom。
任務列隊
單執行緒就意味著,所有的任務都要排隊,前乙個結束,才會執行後面的任務。如果列隊是因為計算量大,cpu 忙不過來,倒也算了。但是更多的時候,cpu 是閒置的,因為 io 裝置處理得很慢,例如 ajax 讀取網路資料。js 設計者便想到,主線程完全可以不管 io 裝置,將其掛起,然後執行後面的任務。等後面的任務結束掉,在反過頭來處理掛起的任務。
好,我們來梳理一下:
只要主線程空了,就會讀取任務列隊,這就是 js 的執行機制,也被稱為 event loop(事件迴圈)。
閉包 原型 原型鏈
1.閉包 函式內部的函式 2.優點 1.隔離作用域,防止汙染全域性 2.有自己的獨立變數 3.快取 3.缺點 1.不容易垃圾 2.消耗記憶體 4.原型 1.函式有乙個prototype物件,可以通過函式的原型物件來實現繼承 2.原型prototype物件上有乙個constructor屬性。是建構函式...
閉包和原型鏈
閉包 簡單的理解就是 閉包就是能夠讀取其他函式內部變數的函式,它兩個最大的用處 可以讀取函式內部的變數,讓變數的值始終保持在記憶體中。閉包與它的詞法環境綁在一起,因此閉包讓我們能夠從乙個函式內部訪問其外部函式的作用域 閉包特點 函式a巢狀函式b 函式b能訪問函式a的變數 最後返回函式b 函式b就是閉...
作用域鏈與原型鏈
1 什麼是作用域鏈 當 在乙個環境中執行時,會建立變數物件的乙個作用域鏈。由子級作用域返回父級作用域中尋找變數,就叫做作用域鏈。作用域鏈中的下乙個變數物件來自包含環境,也叫外部環境。而再下乙個變數物件則來自下乙個包含環境,一直延續到全域性執行環境。全域性執行環境的變數物件始終都是作用域鏈中的最後乙個...