構(gòu)造函數(shù)原型與原型鏈

一砌梆、構(gòu)造函數(shù)

構(gòu)造函數(shù)的函數(shù)名以大寫字母為開頭课蔬。
JavaScript規(guī)定弛说,一個函數(shù)可以用new關(guān)鍵字來調(diào)用麸拄。那么此時將按順序發(fā)生四件事情:

(1)隱秘的創(chuàng)建一個新的空對象

(2)將這個函數(shù)里面的this綁定到剛才創(chuàng)建隱秘新對象上

(3)執(zhí)行函數(shù)體里面的語句

(4)返回這個新的對象

function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
    console.log(11);//會打印因為第三步執(zhí)行了函數(shù)體內(nèi)的語句
}
var xiaoming = new People();//11
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}
xiaoming.name;//小明

我們發(fā)現(xiàn)People不僅能執(zhí)行還能通過new返回一個對象派昧,我們就稱呼像People這樣的函數(shù)為構(gòu)造函數(shù),而xiaoming這樣返回出來的對象就稱呼為構(gòu)造函數(shù)的實例拢切,(這里就是People的實例)

1.1注意事項

1.1.1如果構(gòu)造函數(shù)內(nèi)沒有this將返回一個空對象。

function People(){
for(let i = 0; i < 3; i++) {
  console.log(i);
  }
}
var xiaoming = new People();//0,1,2
console.log(xiaoming);//{}

1.1.2 構(gòu)造函數(shù)構(gòu)造函數(shù)中秆吵,不允許出現(xiàn)return語句

但出現(xiàn)return語句的時候淮椰,如果return的是一個對象,那么通過new放回出來的對象就是return出來的對象纳寂,如果是簡單數(shù)據(jù)類型主穗,返回正常情況下該返回的實例。

//正常情況
function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
}
var xiaoming = new People();
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}

//return 一個對象
function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
return {a: 1, b: 2}
}
var xiaoming = new People();
console.log(xiaoming);// {a: 1, b: 2}

//return 簡單數(shù)據(jù)類型
function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
return 1
}
var xiaoming = new People();
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}

二原型

原型的特點:
1毙芜、原型也是對象忽媒,原型是函數(shù)對象的一個屬性
2、原型自帶constructor屬性腋粥, constructor指定構(gòu)造函數(shù)
3晦雨、構(gòu)造函數(shù)創(chuàng)建出的對象會繼承原型的屬性和方法

每個函數(shù)都有原型。

function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
}
People.prototype.sayHello = function() {
console.log('你好');
}
var xiaoming = new People();
console.log(typeof People.prototype);//Object
console.log(People.prototype.constructor);//People
xiaoming.sayHello();//你好

function test(){};
console.log(test.prototype)//{}

2.1實例對象的原型

當一個對象被new出來時隘冲,這個實例對象會自帶一個_proto_屬性,這個屬性指向生出這個實例的構(gòu)造函數(shù)的原型闹瞧。

function People(){
    this.name = "小明";
    this.age = 18;
    this.sex = "男"; 
}
var xiaoming = new People();
console.log(xiaoming.__proto__ === People.prototype)//true

注意:__proto__前后各有兩個英文狀態(tài)下的下劃線
我們把構(gòu)造函數(shù)的原型叫做構(gòu)造函數(shù)實例的原型對象。
__proto__屬性展辞,是Chrome自己的屬性奥邮,別的瀏覽器不兼容,但是別的瀏覽器也有原型對象,只不過不能通過proto進行訪問而已洽腺。

三應(yīng)用

如果我們吧方法定義在構(gòu)造函數(shù)內(nèi)部時

function People(name,age){
    this.name = name;
    this.age = age;
   this.sayHello = function() {
    console.log('hello')
  }
}
let xiaoming = new People('小明',14);
let xiaogang = new People('小剛', 14);
xiaoming .sayHello;//hello
xiaogang.sayHello;//hello

在new一個xiaoming的時候,構(gòu)造函數(shù)中的代碼順序執(zhí)行,綁定了name,age,sayHello,new一個xiaogang的時候也是如此,你有沒有發(fā)現(xiàn),不同的對象擁有相同他方法.因此可以將復(fù)用的方法放在原型對象上.
以避免消耗內(nèi)存脚粟。

function People(name,age){
    this.name = name;
    this.age = age;
}
People.prototype.sayHello = function() {
  console.log('hello')
   }
let xiaoming = new People('小明',14);
let xiaogang = new People('小剛', 14);
xiaoming .sayHello();//hello
xiaogang.sayHello();//hello
console.log(xiaoming.sayHello === xiaogang.sayHello);//true

四常見的創(chuàng)建對象的模式

4.1工廠模式

function test(name,age) {
let o = new Object();
  o.name = name;
  o.age = age;
  o.sayHello = function() {
    console.log('helllo')
   };
return o
};
let test1 = test('first',1);
let test2 = test('second', 2);

??模式雖然解決了創(chuàng)建多個相似對象的問題,但沒有解決對象標識的問題蘸朋。

4.2構(gòu)造函數(shù)模式

function Test(name,age) {
  this.name = name;
  this.age = age;
  this.sayHello = function() {
    console.log('helllo')
   };
};
let test1 = test('first',1);
let test2 = test('second', 2);

相對于工廠模式,構(gòu)造函數(shù)模式的優(yōu)點:
1核无、沒有顯式地創(chuàng)建對象

2、直接將屬性和方法賦給了 this 對象

3度液、沒有 return 語句

構(gòu)造函數(shù)模式的缺點: 就是每個?法都要在每個實例上重新創(chuàng)建?遍厕宗。

4.3.原型函數(shù)模式

function People(){}
People.prototype = {
constructor: People,
 name : 'xiaoming',
 age : 18,
sayHello () {
console.log('hello')
  }

}
let xiaoming = new People();
let xiaogang = new People();

原型模式的好處是所有對象實例共享它的屬性和方法(即所謂的共有屬性),
缺點:就是省略了為構(gòu)造函數(shù)傳遞初始值參數(shù),導致所有的實例對象都是相同的屬性和方法堕担,其主要問題在于已慢,如果原型上存在引用類型的值的屬性時:

function People(){}
People.prototype = {
constructor: People,
 name : 'xiaoming',
 age : 18,
sayHello () {
console.log('hello')
  },
friend : ['a']

}
let xiaoming = new People();
let xiaogang = new People();
xiaoming.friend.push('b');
console.log(xiaoming.friend);//['a', 'b']
console.log(xiaogang.friend);//['a', 'b']

4.4、混合模式(構(gòu)造函數(shù)模式+原型模式)

構(gòu)造函數(shù)模式用于定義實例屬性霹购,原型模式用于定義方法和共享的屬性

function People(name,age){
this.name = name;
this.age = age

}
People.prototype = {
constructor: People,
sayHello () {
console.log('hello')
  }

}
let xiaoming = new People('小明',1);
let xiaogang = new People('小剛',2);

五原型鏈

只要是對象佑惠,一定有原型對象(除了Object.prototype),而一個實例通過__proto__屬性可以知道它的原型對象齐疙,也就是它的構(gòu)造函數(shù)的原型膜楷。而它的原型對象也是對象,那么它的原型對象應(yīng)該也有原型對象贞奋。

function People() {};
let people = new People;
console.log(people.__proto__ );
console.log(people.__proto__ .__proto__.constructor);//Object
console.log(people.__proto__ .__proto__.__proto__);//null

Object是一個函數(shù)赌厅,是系統(tǒng)內(nèi)置的構(gòu)造函數(shù),用于創(chuàng)造對象的轿塔。Object.prototype是所有對象的原型鏈終點特愿。

所以,當我們在一個對象上打點調(diào)用某個方法的時候勾缭,系統(tǒng)會沿著原型鏈去尋找它的定義揍障,一直找到Object.prototype。

function People() {};
let people = new People;
Object.prototype.sayHello = function() {
console.log('hello')
};
people.sayHello();

Object.prototype是所有對象的原型鏈的終點俩由,所以我們直接給Object.prototype增加一個方法毒嫡,那么所有的對象都能調(diào)用這個方法.

5.1引用類型的構(gòu)造函數(shù)

我們可以利用原型鏈的機制,給數(shù)組對象增加方法.

所有的引用類型值幻梯,都有內(nèi)置構(gòu)造函數(shù)兜畸。比如

new Object()

new Array()

new Function()

new RegExp()

new Date();
以數(shù)組為例子礼旅;

let arr = [1,2,3,45,6];
console.log(arr.__proto__ === Array.prototype)//true
console.log(arr.__proto__.__proto__.constructor === Object)//true
let arr = [1,2,3,45,6];
let max = -Infinity;
arr.__proto__.max = function() {
for(let i = 0; i < arr.length; i++) {
      if(arr[i] >= max) {
      max = arr[i]
    }
  }
  return max
}
arr.max()//45

5.2基本數(shù)據(jù)類型的包裝類

基本類型值膳叨,也有包裝類型。所謂包裝類型痘系,就是它的構(gòu)造函數(shù)菲嘴。
1、new Number()
2、new String()
3龄坪、new Boolean()

let num = 10; 
console.log(num.__proto__ === Number.prototype)//true
console.log(num.__proto__.__proto__.constructor === Object)//true

let str= '10'; 
console.log(str.__proto__ === String.prototype)//true
console.log(str.__proto__.__proto__.constructor === Object)//true

let bol= true; 
console.log(bol.__proto__ === Boolean.prototype)//true
console.log(bol.__proto__.__proto__.constructor === Object)//true

let un= undefined; 
console.log(un.__proto__.constructor)//報錯

let nu= null; 
console.log(nu.__proto__.constructor)//報錯

六對象與屬性

6.1打點調(diào)用

var obj = {
    a : 1,
    b : 2,
    c : 3
}
obj.__proto__ = {
    d : 4
}


console.log(obj.a); //1
console.log(obj.b); //2
console.log(obj.c); //3
console.log(obj.d); //4

6.2in

var obj = {
    a : 1,
    b : 2,
    c : false
}
obj.__proto__ = {
    d: 20
}
console.log("a" in obj);    //true
console.log("b" in obj);    //true
console.log("c" in obj);    //true
console.log("d" in obj);    //true

in和打點調(diào)用昭雌,如果自己或者只要在原型鏈上有這個方法就會被查找到并返回true。

通過for...in循環(huán)調(diào)用可枚舉的屬性健田。

var obj = {
    a : 1,
    b : 2,
    c : false
}
obj.__proto__ = {
    d: 20
}
for( let k in obj) {
console.log(k)//a,b,c,d.
}

6.2 hasOwnProperty

這個方法定義在了Object.prototype對象上面烛卧,所以任何一個Object都能夠擁有這個方法。

var obj = {
    a : 1,
    b : 2,
    c : 3
}
obj.__proto__ = {
    d : 4
}

console.log(obj.hasOwnProperty("a")); //true
console.log(obj.hasOwnProperty("b")); //true
console.log(obj.hasOwnProperty("c")); //true
console.log(obj.hasOwnProperty("d")); //false,在原型鏈上不在實例上

把自己身上的屬性輸出:

for(let k in obj){
    obj.hasOwnProperty(k) && console.log(k);
}

把不在自己身上的屬性輸出:

var obj = {
    a : 1,
    b : 2,
    c : 3
}
obj.__proto__ = {
    d : 4
}

for(let k in obj){
if(!obj.hasOwnProperty(k) && k in obj) {
  console.log(k)//d
  }

}

6.3 定義多個屬性O(shè)bject.defineProperties()

    var test = {};
    Object.defineProperties(test, {
        _name: {
            value: 'chen'
       },
        name: {
            get: function() {
                return this._name;
            },
            set: function(newvalue) {
                if (typeof newvalue != 'string') {
                    throw new Error(TypeError);
                } else {
                    this._name = newvalue;
                }
            }
        }
    })
console.log(test.name);//chen
test.name = 10;//typeerror;
test.name = 'aa';
console.log(test.name);//aa

讀取屬性的描述對象Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor(test, 'name')
//{enumerable: false, configurable: false, get: ?, set: ?}

6.4 instanceof

function Boy(){};
function Girl(){};
Girl.prototype = new Boy();
let xiaohong = new Girl();
console.log(xiaohong.constructor)//Boy;
console.log(xiaohong instanceof(Boy))//true
console.log(xiaohong instanceof(Girl))//true

首先妓局,實例會繼承原型對象的屬性和方法(當然包括constructor屬性)总放;這里Girl.prototype上的constructor本來應(yīng)該指向Girl,但由于
Girl.prototype = new Boy()好爬,new Boy()實例繼承了Boy.prototype上的constructor:Boy,最后new Girl()也繼承了它原型對象new Boy()的constructor局雄。所以xiaohong.constructor為Boy。

instanceof 運算符的機理: 遍訪xiaohong這個對象的原型鏈上的每個原型對象存炮,如果遍訪到這個原型對象炬搭,是某個構(gòu)造函數(shù)的prototype,那么就認為xiaohong是這個構(gòu)造函數(shù)的實例穆桂,返回true宫盔。

6.5檢測數(shù)據(jù)類型

Object.prototype.toString.call(123);  //"[object Number]"
Object.prototype.toString.call("123");  //"[object String]"
Object.prototype.toString.call(true);  //"[object Boolean]"
Object.prototype.toString.call(undefined);  //"[object Undefined]"
Object.prototype.toString.call(null);  //"[object Null]"
Object.prototype.toString.call(function(){});  //"[object Function]"
Object.prototype.toString.call([]);  //"[object Array]"
Object.prototype.toString.call({});  //"[object Object]"

七 繼承

7.1原型鏈繼承

簡單來說就是把父類的實例作為子類的原型。

function People(name){
    this.name = name;
}
People.prototype.sayHello = function(){
    alert("你好我是" + this.name);
}
function Student(name,xuehao){
    this.name = name;
    this.xuehao = xuehao;
}
Student.prototype = new People('大明');
var xiaohong = new Student("小紅",1001);

這樣子類既可以繼承父類的屬性享完、方法又可以在自己追加方法灼芭。
不過當父類原型上具有引用類型值的屬性時,會有大問題:

function People(name){
    this.name = name;
}
People.prototype.arr= [1,2,3]
function Student(name,xuehao){
    this.name = name;
    this.xuehao = xuehao;
}
Student.prototype = new People('大明');
var xiaohong = new Student("小紅",1001);


//**注意這里**
xiaohong.arr.push(4);
console.log(xiaohong.arr);//[1,2,3,4]
var xiaoming = new Student("小明",1001);
console.log(xiaoming.arr);//[1,2,3,4]

7.2構(gòu)造函數(shù)繼承

function People(name){
    this.name = name;
}
People.prototype.sayHello = function(){
    alert("你好我是" + this.name);
}

function Student(name,xuehao){
//核心
    People.call(this,name);
    this.xuehao = xuehao;
}
Student.prototype.study = function(){
    alert("好好學習般又,天天向上");
}

var xiaohong = new Student("小紅",1001);

之前說過姿鸿,用call先執(zhí)行函數(shù),然后在執(zhí)行函數(shù)的時候改變this指向倒源,
那么 People.call(this,name),也就好理解了。
缺點句狼,所有的屬性和方法只能定義在構(gòu)造函數(shù)身上笋熬,無法實現(xiàn)函數(shù)的復(fù)用,子類也不能訪問到父類的原型腻菇。

7.3. 組合繼承

就是將原型鏈繼承和構(gòu)造函數(shù)繼承組合在一起;繼承兩個優(yōu)點胳螟。

function People(name){
    this.name = name;
}
function Student(name,xuehao){
//構(gòu)造繼承
    People.call(this,name);
    this.xuehao = xuehao;
}

//核心語句,
//原型繼承
Student.prototype = new People('大明');

Student.prototype.study = function(){
    alert("好好學習筹吐,天天向上");
}

var xiaohong = new Student("小紅",1001);

xiaohong.sayHello();

缺點:
調(diào)用了兩次父類構(gòu)造函數(shù)糖耸,一次是在創(chuàng)建子類原型時,一次是在子類構(gòu)造函數(shù)中丘薛。

7.4. 寄生組合繼承

function People(name){
    this.name = name;
}
function Student(name,xuehao){
    People.call(this,name);
    this.xuehao = xuehao;
}


function Fn() {};
Fn.prototype = People.prototype
Student.prototype = new Fn();//核心語句嘉竟,

Student.prototype.study = function(){
    alert("好好學習,天天向上");
}

var xiaohong = new Student("小紅",1001);

xiaohong.sayHello();

這里只調(diào)用了一次People構(gòu)造函數(shù),避免了組合繼承的缺點舍扰。

7.5. 圣杯模式

圣杯模式就是將借用的構(gòu)造函數(shù)封裝一下

function inherit(People,Student){
    function Fn(){}
    Fn.prototype = People.prototype
    Student.prototype = new Fn();
    Student.prototype.constructor = Student;//增強對象
}
inherit(People,Student )
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倦蚪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子边苹,更是在濱河造成了極大的恐慌陵且,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件个束,死亡現(xiàn)場離奇詭異慕购,居然都是意外死亡,警方通過查閱死者的電腦和手機茬底,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門沪悲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人桩警,你說我怎么就攤上這事可训。” “怎么了捶枢?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵握截,是天一觀的道長。 經(jīng)常有香客問我烂叔,道長谨胞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任蒜鸡,我火速辦了婚禮胯努,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逢防。我一直安慰自己叶沛,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布忘朝。 她就那樣靜靜地躺著灰署,像睡著了一般。 火紅的嫁衣襯著肌膚如雪局嘁。 梳的紋絲不亂的頭發(fā)上溉箕,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音悦昵,去河邊找鬼肴茄。 笑死,一個胖子當著我的面吹牛但指,可吹牛的內(nèi)容都是我干的寡痰。 我是一名探鬼主播抗楔,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼氓癌!你這毒婦竟也來了谓谦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤贪婉,失蹤者是張志新(化名)和其女友劉穎反粥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疲迂,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡才顿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尤蒿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郑气。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腰池,靈堂內(nèi)的尸體忽然破棺而出尾组,到底是詐尸還是另有隱情,我是刑警寧澤示弓,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布讳侨,位于F島的核電站,受9級特大地震影響奏属,放射性物質(zhì)發(fā)生泄漏跨跨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一囱皿、第九天 我趴在偏房一處隱蔽的房頂上張望勇婴。 院中可真熱鬧,春花似錦嘱腥、人聲如沸耕渴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萨螺。三九已至,卻和暖如春愧驱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椭盏。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工组砚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掏颊。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓糟红,卻偏偏與公主長得像艾帐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盆偿,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容