prototype
對象屬性,除了 Function.prototype.bind()
,它指向原型。__proto__
對象,它指向建立這個對象的構造函數的原型。其實這個屬性指向了 [[prototype]]
,可是 [[prototype]]
是內部屬性,咱們訪問不到,因此用 __proto__
來訪問。__proto__
來尋找部署改對象的屬性,__proto__
將對象鏈接起來組成 原型鏈。小結:從上圖可看出,一切皆對象,對象都有 __proto__
屬性javascript
new
出來的對象,其 __proto__
(這個對象的屬性)指向建立這個對象的構造函數的原型;而這個構造函數的原型也是對象,它的 __proto__
指向 Object
的原型;function Foo(){}
const f1 = new Foo();
f1.__proto__ => Foo.prototype;
Foo.prototype.__proto__ => Object.prototype;
Object.prototype.__proto__ => null; // 到頭了
複製代碼
const obj = {};
obj.__proto__ => Object.prototype;
Object.prototype.__proto__ => null; // 到頭了
const arr = [];
arr.__proto__ => Array.prototype;
Array.prototype.__proto__ => Object.prototype;
Object.prototype.__proto__ => null; // 到頭了
複製代碼
__proto__
是怎樣的呢?它指向構造函數 Function
的原型:function fn(){}
fn.__proto__ => Function.prototype;
Function.prototype.__proto__ => Object.prototype;
Object.prototype.__proto__ => null; // 到頭了
複製代碼
prototype
屬性,它的 constructor
又指回函數本身fn.prototype.constructor => fn;
複製代碼
let obj = new Object()
;let Con = [].shift.call(arguments)
;obj.__proto__ = Con.prototype
;let result = Con.apply(obj, arguments)
;return typeof result === 'object' ? result : obj
;在調用new
的過程當中會發生以上4件事:java
function create(){
// 建立一個空的對象
let obj = new Object();
// 得到構造函數
let Con = [].shift.call(arguments);
// 連接到原型
obj.__proto__ = Con.prototype;
// 綁定 this,執行構造函數
let result = Con.apply(obj, arguments);
// 確保 new 出來的是個對象
return typeof result === 'object' ? result : obj;
}
複製代碼
對於實例化對象來講,都是經過 new
產生的,不管是 function Foo()
仍是 let x = { y: 1 }
。app
對於建立一個對象來講,推薦使用字面量的方式建立對象(不管性能仍是可讀性)。由於你使用 new Object()
的方式建立對象須要經過做用域鏈一層層找到Object
,可是你使用字面量的方式就沒這個問題了。函數
function Foo(); // function 就是個語法糖,內部等同與 new Function() let x = { y: 1 };
// 這個字面量內部也是使用了new Object();
複製代碼
對於new
來講,還需注意下運算符優先級post
function Foo(){
return this;
}
Foo.getName = function(){
console.log('name');
}
Foo.prototype.getName = function(){
console.log('prototypeName');
}
new Foo.getName(); // name
new Foo().getName(); // prototypeName
複製代碼
new Foo()
的優先級高於new Foo
,因此上面最後2行代碼可劃分執行順序性能
new (Foo.getName());
(new Foo()).getName();
複製代碼
instanceof
可正確判斷對象的類型,由於內部機制是經過判斷對象的原型鏈中是否是能找到類型的 prototype
。下面自我實現一個 instanceof
函數測試
function inof(left, right) {
// 得到類型的原型
right = right.prototype;
// 得到對象的原型
left = left.__proto__;
// 判斷對象的類型是否等於類型的原型
while (true) {
if (left === null) {
return false
}
if (right === left) {
return true
}
left = left.__proto__;
}
}
複製代碼
測試一下ui
function fn(){}
inof(fn, Function); // true
inof(fn, Object); // true
複製代碼
它是一個會讓不少人混淆的概念,讓咱們用幾個場景規則來記住它。this
// 場景一
function fn(){
console.log(this.a);
}
var a = 1;
fn(); // 1
// 場景二
var obj = {
a: 2,
fn: fn
};
obj.fn(); // 2
// 以上兩種場景 `this` 依賴於調用函數前的對象
// 場景三 - 優先級最高的, `this` 只會綁定在 `obj1` 上,不會被任何方式修改 `this` 指向
var obj1 = new fn();
obj1.a = 3;
console.log(obj1.a); // 3
// 場景四 - 利用 call、apply、bind 改變 this,這個優先級僅次於 new
fn.call(obj); // 2
複製代碼
以上 4 種場景弄清楚了,不少代碼中的 this
就不是問題了。spa
function fn() {
return () => {
return () => {
console.log(this);
}
}
}
console.log(fn()()()); // window
複製代碼
箭頭函數中實際上是沒有 this
的,這個函數中的 this
只取決於它外面的第一個不是箭頭函數的函數的 this
。在這個例子中,由於調用 fn
符合前面代碼中的第一個場景,因此 this
是 window
。而且 this
一旦綁定了上下文,就不會被任何代碼改變。