原型 原型鏈 call/apply

原型

原型是function對(duì)象的一個(gè)屬性,它定義了構(gòu)造函數(shù)制造出的對(duì)象的祖先。通過(guò)該構(gòu)造函數(shù)構(gòu)造出來(lái)的對(duì)象,可以繼承該原型的屬性和方法。原型也是對(duì)象

原型知識(shí)講解

舉一個(gè)小栗子

    Person.prototype.lastName="deng";
    function Person(name,sex){
        this.name=name;
        this.sex=sex;
    }
    var person = new Person("xiaoliu",'male');

prototype的講解

通過(guò)上面的代碼我們不難看出,對(duì)象可以訪問(wèn)到訪問(wèn)自己的屬性

然而令人感到好奇的是,通過(guò)person這個(gè)對(duì)象可以訪問(wèn)到Person.prototype上的屬性——其實(shí)這是因?yàn)镻erson.prototype是person對(duì)象的原型(就好像是祖先一樣,子孫可以繼承祖先的屬性和方法),這就是person對(duì)象有l(wèi)astName屬性的原因。

增刪改查講解


  • 原型
實(shí)例對(duì)象

無(wú)論是原型還是實(shí)例對(duì)象都使用統(tǒng)一的方式增加屬性或者方法

        Person.prototype.lastName="deng";
        function Person(name,sex){
            this.name=name;
            this.sex=sex;
        }
        var person = new Person("xiaoliu",'male');
實(shí)例對(duì)象刪除自己的屬性

毫無(wú)疑問(wèn),實(shí)例對(duì)象可以刪除自己的屬性

實(shí)例對(duì)象刪除原型上的屬性

實(shí)例對(duì)象刪除原型上的屬性返回true,但是沒(méi)有刪除成功,這是為什么呢?看下面演示

刪除person沒(méi)有的屬性

刪除對(duì)象上沒(méi)有的屬性都會(huì)返回ture

原型刪除自己的屬性

結(jié)論
實(shí)例對(duì)象可以刪除自己的屬性,原型可以刪除自己的屬性,實(shí)例對(duì)象不能刪除原型上的屬性。至于原型是否可以刪除實(shí)例對(duì)象的屬性——原型上是沒(méi)有實(shí)例對(duì)象的屬性的


  • 兩種方法
    方法一
方法二

第一種情況

先修改原型

成功修改原型上的屬性以及實(shí)例對(duì)象的屬性

后修改實(shí)例對(duì)象

由實(shí)例對(duì)象修改原型中的屬性時(shí),只能成功修改實(shí)例對(duì)象上的屬性,原型中的屬性并沒(méi)有被修改

第二種情況

先修改實(shí)例對(duì)象

結(jié)果同上一個(gè)情況一樣

再修改原型屬性

這是你會(huì)發(fā)現(xiàn)此次修改也只是修改原型上的屬性,實(shí)例對(duì)象上的屬性并沒(méi)有被修改成功,這是為什么呢??——這是因?yàn)閷?shí)例對(duì)象已經(jīng)擁有自己的顯式屬性,所以修改原型上的屬性并不會(huì)影響實(shí)例對(duì)象的屬性

由于講解需要,先把__proto __講了
proto用于查看原型的一個(gè)屬性,而且是隱式屬性
栗子栗子

        Person.prototype.name = 'Sunny';
        function Person() {}
        var person = new Person();
image.png

淺紫色表示隱式屬性,深紫色表示顯式屬性

實(shí)例對(duì)象__proto__屬性

原型__proto__

無(wú)論是實(shí)例對(duì)象上的proto還是原型上的proto屬性最終的原型都是Object,Object的原型是null,person.proto==Person.prototype

修改原型上屬性的兩種方式以及它們的不同之處

  • 第一種
 Person.prototype.name = 'Sunny';

        function Person() {}
        Person.prototype.name="Cherry";

        var person = new Person();
結(jié)果
Person.prototype.name = 'Sunny';
        function Person() {}
        var person = new Person();
        Person.prototype.name="Cherry";
結(jié)果

顯而易見(jiàn),上面這兩個(gè)栗子只是原型修改一個(gè)屬性

  • 第二種 稍微變換一下
        Person.prototype.name='sunny';
        function Person() { 
      /*內(nèi)部是這樣子的*/
           //var this={
           //__proto__:Person.prototype
           //}
        var person = new Person();
        Person.prototype={
            name:'cherry'
        }

這個(gè)栗子先創(chuàng)建了一個(gè)實(shí)例對(duì)象,然后通過(guò)不同的方式修改Person.prototype中的name屬性,猜一猜Person.prototype.name和person.name的值是什么?

結(jié)果

實(shí)例對(duì)象和原型(也是一個(gè)對(duì)象),它們擁有對(duì)象的特點(diǎn)——存儲(chǔ)的是數(shù)據(jù)的引用,當(dāng)兩個(gè)對(duì)象是同一個(gè)引用時(shí),修改一個(gè)對(duì)象的引用值時(shí),不會(huì)影響另外一個(gè)對(duì)象的值。現(xiàn)在person.proto和Person.prototype指向同一個(gè)空間,然后Person.prototype換一個(gè)空間,而原來(lái)的person.proto沒(méi)有變,所以person.name還是為sunny,再舉一個(gè)更簡(jiǎn)單的栗子吧~

      var obj = {
            name: 'a'
        };
        var obj1 = obj;
        obj = {
            name: 'b'
        }
結(jié)果

現(xiàn)在應(yīng)該懂了修改引用中的值的妙處了吧~

考一考你

     Person.prototype.name = 'Sunny';
        function Person() {
       /*內(nèi)部是這樣子的*/
           //var this={
           //__proto__:Person.prototype
           //}}
        Person.prototype = {
            name: 'cherry'
        }
        var person = new Person();
結(jié)果

想想預(yù)編譯過(guò)程以及函數(shù)的執(zhí)行順序(什么時(shí)候才調(diào)用構(gòu)造函數(shù))就很容易得出結(jié)果了~

constructor講解
construcor可以查看一個(gè)對(duì)象的構(gòu)造函數(shù),構(gòu)造器是一個(gè)隱式屬性
一個(gè)栗子引入

Person.prototype.name='sunny';
        function Person() {  }
        var person = new Person();


而且constructor可以被修改

  function Car(){}
        
        Person.prototype.name='sunny';
        function Person() {  
            //var this ={
                  //constructor:Person
             //}
}
        var person = new Person();

        person.constructor=Car;
結(jié)果

原型鏈

//Grand.prototype.__proto__->Object.prototype
//Object.prototype.__proto__->null
   Grand.prototype.lastName = "deng";
        function Grand() {}
        var grand = new Grand();

        Father.prototype=grand;
        function Father() {}
        var father = new Father();
        
        Son.prototype=father;
        function Son() {}
        var son = new Son();
       

son實(shí)例對(duì)象查找lastname屬性時(shí)會(huì)根據(jù)原型鏈一層層往上找,直到找到為止,或者找不到返回undefined

原型鏈上的增刪改查

原型鏈上的增刪改查和原型的增刪改查是差不多的

  • 這里講一個(gè)修改的小特例
    在father身上加一個(gè)引用值
    function Father() {
            this.fortune={
                fo1:"visa"
            }
        }
        var father = new Father();

        Son.prototype=father;
        function Son() {}
        var son = new Son();

這時(shí)通過(guò)son修改fortune


方法一
方法二

上面兩種方式不同,第一種son直接修改person.fortune,很顯然,只修改了son.fortune的值,而第二種方式是直接操作fortune引用值,顯然是引用值增加一個(gè)屬性,只是引用值的修改。

補(bǔ)充一個(gè)知識(shí)

 var obj = {}
        var obj1=new Object();
        //obj1._proto_  -->Object.prototype
image.png

第一種var obj = {}是對(duì)象自變量的創(chuàng)建形式,上面的兩種創(chuàng)建方式是一樣的,用var obj={}的方式系統(tǒng)會(huì)默認(rèn)用new Object()的方式創(chuàng)建,平時(shí)再構(gòu)造對(duì)象時(shí)建議用對(duì)象自變量的創(chuàng)建方式,即var obj={}

new的作用
1、創(chuàng)建一個(gè)新的對(duì)象
2、將構(gòu)造函數(shù)的this指向這個(gè)新對(duì)象
3、返回這個(gè)新對(duì)象

Object.create()
創(chuàng)建對(duì)象的方法

var obj = Object.create(原型)

 var obj= {name:"sunny",age:123};
        var obj1=Object.create(obj);

obj1的原型就是obj,所以obj1繼承obj的屬性

再舉一個(gè)例子

  Person.prototype.name="sunny";
        function Person(){

        }
        var person=Object.create(Person.prototype);

  Person.prototype.name="sunny";
        function Person(){

        }
     var person=new Person();

的結(jié)果是一樣的,但是在person的構(gòu)造函數(shù)中定義自己的屬性就不一樣了

一個(gè)錯(cuò)誤的概念
全部對(duì)象最終都會(huì)繼承自O(shè)bject.prototype

正解

對(duì)象的原型只能是一個(gè)Object或者是null,所以我構(gòu)造一個(gè)沒(méi)有原型的對(duì)象,如下

沒(méi)有原型

結(jié)論
絕大多數(shù)對(duì)象最終都會(huì)繼承自O(shè)bject.prototype,有的對(duì)象沒(méi)有原型

再補(bǔ)充一下
null和undefiend不能調(diào)用toString()

因?yàn)樗鼈儧](méi)有原型

call/apply

改變this指向
call

function test(){}
test() //其實(shí)執(zhí)行會(huì)默認(rèn)為test.call()

test()和test.call()沒(méi)有區(qū)別

  function Person(name,age){
            //this=obj
            this.name=name;
            this.age=age
        }
        //沒(méi)有new,this指向window
        var person = new Person('deng',100);

        var obj={}
        Person.call(obj,'cheng',200);//第二,三...是參數(shù)

call()就是改變this的指向的,上面的栗子就是讓obj使用Person的方法構(gòu)造對(duì)象,這時(shí)person構(gòu)造函數(shù)的this為obj,相當(dāng)于obj.name=name,obj.age=age;

傳參
需要把實(shí)參按照形參的個(gè)數(shù)傳進(jìn)去
用法
當(dāng)兩個(gè)對(duì)象含有相同的屬性時(shí),但是另一個(gè)對(duì)象的屬性比較多(功能涵蓋)

        function Person(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
        }

        function Student(name,age,sex,tel,grade){
            this.name=name;
            this.age=age;
            this.sex=sex;
            this.tel=tel;
            this.grade=grade;
        }
 var student= new Student('Sunny',123,'male',139,2019);

上面構(gòu)造對(duì)象的方式顯得累贅,我們可以用call的方式使用另一個(gè)對(duì)象的構(gòu)造方法

        function Person(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
        }

        function Student(name,age,sex,tel,grade){
         Person.call(this,name,age,sex);
            this.tel=tel;
            this.grade=grade;
        }
        var student= new Student('Sunny',123,'male',139,2019);

結(jié)論
對(duì)象名.call(對(duì)象名,參數(shù)...)可以實(shí)現(xiàn)借用別的對(duì)象的屬性來(lái)構(gòu)造本對(duì)象的屬性

apply
使用方式:對(duì)象名.(this,[參數(shù)])
區(qū)別
傳參列表不同,call需要把實(shí)參按照形參的個(gè)數(shù)傳進(jìn)去,apply需要傳一個(gè)arguments

最后看一張圖來(lái)縷一縷自己的思路吧

function Add() {}
var add=new Add();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容