原型對象與原型鏈

原型的概述程序員

咱們建立的每一個函數都有一個 prototype(原型)屬性,這個屬性是一個對象,它的用途是包含能夠由特定類型的全部實例共享的屬性和方法。瀏覽器

邏輯上能夠這麼理解:prototype 經過調用構造函數而建立的那個對象的原型對象。函數

使用原型的好處可讓全部對象實例共享它所包含的屬性和方法。也就是說,沒必要在構造函數中定義對象信息,而是能夠直接將這些信息添加到原型中測試

注意:若是函數做爲普通函數調用prototype沒有任何做用this

 

 

使用原型建立對象spa

function Box() {}       //聲明一個構造函數,函數體內什麼都沒有,若是有叫作實例屬性,實例方法
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

/*比較一下原型內的方法地址是否一致:*/
var box1 = new Box();
var box2 = new Box();
console.log(box1) // Box {}
//向box1中添加a屬性,a屬性是box1本身的,其餘對象沒有
box1.a = "我是box1中的a";
console.log(box1) // Box { a: '我是box1中的a' }
console.log(box1.a) // 我是box1中的a
console.log(box2.a) // undefined, box2中沒有a這個屬性

console.log(box1.run()) // Lee100運行中...
console.log(Box.prototype) // Box { name: 'Lee', age: 100, run: [Function] }
console.log(box1.run == box2.run); //true,方法的引用地址保持一致

當函數以構造函數的形式調用時,它所建立的對象中都會有一個隱含的屬性,指向該構造函數的原型對象,咱們能夠經過__proto__來訪問該屬性prototype

function fn() {

}
console.log(fn.__proto__) // [Function]

原型對象就至關於一個公共的區域,全部同一個類的實例均可以訪問到這個原型對象,咱們能夠將對象中共有的內容,統一設置到原型對象中指針

之後咱們建立構造函數時,能夠將這些對象共有的屬性和方法,統一添加到構造函數的原型對象中,這樣不用分別爲每個對象添加,也不會影響到全局做用域,就可使每一個對象都具備這些屬性和方法了code

當咱們訪問對象的一個屬性或方法時,它會先在對象自身中尋找,若是有則直接使用,若是沒有則會去原型對象中尋找,若是找到則直接使用對象

 

 

 

原型對象的原理

在原型模式聲明中,多了兩個屬性,這兩個屬性都是建立對象時自動生成的。

__proto__屬性是實例指向原型對象的一個指針,它的做用就是指向構造函數的原型屬性 constructor。經過這兩個屬性,就能夠訪問到原型裏的屬性和方法了。

IE 瀏覽器在腳本訪問__proto__會不能識別,火狐和谷歌瀏覽器及其餘某些瀏覽器均能識別。雖然能夠輸出,但沒法獲取內部信息。

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
var box2 = new Box();

console.log(box1.prototype);//這個屬性是一個對象,訪問不到:undefined
console.log(box1.__proto__); //這個屬性是一個指針指向prototype原型對象,打印結果是[object Object]  在IE中結果是undefined

// constructor是構造函數的屬性,獲取構造函數自己
//做用是被原型指針定位,而後等到構造函數自己,其實就是對象實例對應的原型對象
console.log(box1.constructor); // [Function: Box]

console.log(box1.age);//能夠直接訪問原型對象中的屬性和方法,由於底層會自動調用prototype和__proto__和constructor等屬性

判斷一個對象是否指向了該構造函數的原型對象(即判斷一個對象實例是否是指向了對象的原型對象)可使用 isPrototypeOf()方法來測試。

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
var box2 = new Box();
console.log(box1.age);//結果打印出age的值:100
console.log(Box.prototype.isPrototypeOf(box1)); //只要實例化對象,即都會指向:true

每一個函數都有一個prototype屬性, 它默認指向一個Object空對象(即稱爲: 原型對象),原型對象中有一個屬性constructor, 它指向函數對象

// 每一個函數都有一個prototype屬性, 它默認指向一個對象(即稱爲: 原型對象)
console.log(Date.prototype, typeof Date.prototype) // Date {} 'object'
function fn() {

}
console.log(fn.prototype, typeof fn.prototype) // fn {} 'object'

// 原型對象中有一個屬性constructor, 它指向函數對象
console.log(Date.prototype.constructor===Date) // true
console.log(fn.prototype.constructor===fn) // true

 

 

 

 

 

原型模式的執行流程

先查找構造函數實例裏的屬性或方法,若是有,馬上返回;
若是構造函數實例裏沒有,則去它的原型對象裏找,若是有,就返回;

雖然咱們能夠經過對象實例訪問保存在原型中的值,但卻不能經過對象實例重寫原型中的值。

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
console.log(box1.name); //Lee,原型裏的值
box1.name = 'Jack';
console.log(box1.name); //Jack,就近原則,
var box2 = new Box();
console.log(box2.name); //Lee,原型裏的值,沒有被 box1 修改

若是想要 box1 也能在後面繼續訪問到原型裏的值,能夠把構造函數裏的屬性刪除便可,具體以下

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
console.log(box1.name); //Lee,原型裏的值
box1.name = 'Jack';
console.log(box1.name); //Jack,就近原則,訪問的是box1本身的name屬性

delete box1.name; //刪除box1實例中屬性
console.log(box1.name); // box1實例屬性name被刪除了,找到的是原型裏面的name:Lee
Box.prototype.name = 'kkk'//覆蓋原型中name屬性的值
console.log(box1.name);//結果是kkk
delete Box.prototype.name;//刪除原型中的屬性值,以後結果是undefined
console.log(box1.name);

如何判斷屬性是在構造函數的實例裏,仍是在原型裏?可使用 hasOwnProperty()函數來驗證

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
console.log(box1.name); //Lee,原型裏的值
box1.name = 'Jack';
console.log(box1.name); //Jack,就近原則,
console.log(box1.hasOwnProperty('name'));//判斷實例是否存在指定屬性,實例裏有返回 true,不然返回 false

使用in檢查對象中是否含有某個屬性時,若是對象中沒有可是原型中有,也會返回true

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

var box1 = new Box();
console.log('name' in box1); //true,存在實例中或原型

咱們能夠經過hasOwnProperty() 方法檢測屬性是否存在實例中,也能夠經過 in 來判斷實例或原型中是否存在屬性。那麼結合這兩種方法,能夠判斷原型中是否存在屬性。

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};


var box1 = new Box();
console.log(box1.hasOwnProperty('name'));
console.log('name' in box1);
//若是第一個爲false說明實例中沒有該屬性,而第二個爲true說明屬性存在原型中
//若是第一個爲true,說明屬性存在實例中

也能夠定義一個函數來判段,原理是同樣的

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};
function isProperty(object, property) {       //判斷原型中是否存在屬性
  return !object.hasOwnProperty(property) && (property in object);
}

var box1 = new Box();

console.log(isProperty(box1, 'name')) //true,若是原型有

 

 

 

顯式原型與隱式原型

每一個函數function都有一個prototype,即顯式原型(在定義函數時自動添加的, 默認值是一個空Object對象)

每一個實例對象都有一個__proto__,可稱爲隱式原型(建立對象時自動添加的, 默認值爲構造函數的prototype屬性值)

function Fn() {}
var fn = new Fn()
console.log(Fn.prototype, fn.__proto__) // Fn {} Fn {}
console.log(Fn.prototype === fn.__proto__) // true,構造函數的顯示原型的值就是實例對象的隱式原型的值

程序員能直接操做顯式原型, 但不能直接操做隱式原型(ES6以前)

 

 

 

原型鏈概述

原型對象也是對象,因此它也有原型,當咱們使用一個對象的屬性或方法時,會如今自身中尋找,自身中若是有,則直接使用

若是沒有則去原型對象中尋找,若是原型對象中有,則使用,若是沒有則去原型的原型中尋找,直到找到Object對象的原型

Object對象的原型沒有原型,若是在Object原型中依然沒有找到,則返回undefined

function Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};


var box1 = new Box();
console.log(box1); //Box {}
console.log(box1.__proto__); // Box { name: 'Lee', age: 100, run: [Function] }
console.log(box1.__proto__.__proto__); // {}
console.log(box1.__proto__.__proto__.__proto__); // 找到Object對象(Object對象的原型沒有原型),因此是null,
console.log(box1.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true
console.log('name' in box1);

原型鏈(圖解)

訪問一個對象的屬性時,先在自身屬性中查找,找到返回,若是沒有, 再沿着__proto__這條鏈向上查找, 找到返回,若是最終沒找到, 返回undefined

別名: 隱式原型鏈,做用: 查找對象的屬性(方法)

function Fn() {
  this.test1 = function () {
    console.log('test1()')
  }
}
Fn.prototype.test2 = function () {
  console.log('test2()')
}
var fn = new Fn()

fn.test1()
fn.test2()
console.log(fn.toString())
console.log(fn.test3)//undefined
fn.test3() // 報錯:fn.test3 is not a function

構造函數/原型/實體對象的關係(圖解)

var o1 = new Object();
var o2 = {};

構造函數/原型/實例對象的關係2(圖解)

function Foo(){  }

 

 

 

 

原型測試題

var A = function() {}
A.prototype.n = 1

var b = new A()

A.prototype = {
  n: 2,
  m: 3
}

var c = new A()
console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3

相關文章
相關標籤/搜索