原型 原型链 call/apply

原型

原型是function对象的一个属性,它定义了构造函数制造出的对象的祖先。通过该构造函数构造出来的对象,可以继承该原型的属性和方法。原型也是对象

原型知识讲解

举一个小栗子

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

prototype的讲解

通过上面的代码我们不难看出,对象可以访问到访问自己的属性

然而令人感到好奇的是,通过person这个对象可以访问到Person.prototype上的属性——其实这是因为Person.prototype是person对象的原型(就好像是祖先一样,子孙可以继承祖先的属性和方法),这就是person对象有lastName属性的原因。

增删改查讲解


  • 原型
实例对象

无论是原型还是实例对象都使用统一的方式增加属性或者方法

        Person.prototype.lastName="deng";
        function Person(name,sex){
            this.name=name;
            this.sex=sex;
        }
        var person = new Person("xiaoliu",'male');
实例对象删除自己的属性

毫无疑问,实例对象可以删除自己的属性

实例对象删除原型上的属性

实例对象删除原型上的属性返回true,但是没有删除成功,这是为什么呢?看下面演示

删除person没有的属性

删除对象上没有的属性都会返回ture

原型删除自己的属性

结论
实例对象可以删除自己的属性,原型可以删除自己的属性,实例对象不能删除原型上的属性。至于原型是否可以删除实例对象的属性——原型上是没有实例对象的属性的


  • 两种方法
    方法一
方法二

第一种情况

先修改原型

成功修改原型上的属性以及实例对象的属性

后修改实例对象

由实例对象修改原型中的属性时,只能成功修改实例对象上的属性,原型中的属性并没有被修改

第二种情况

先修改实例对象

结果同上一个情况一样

再修改原型属性

这是你会发现此次修改也只是修改原型上的属性,实例对象上的属性并没有被修改成功,这是为什么呢??——这是因为实例对象已经拥有自己的显式属性,所以修改原型上的属性并不会影响实例对象的属性

由于讲解需要,先把__proto __讲了
proto用于查看原型的一个属性,而且是隐式属性
栗子栗子

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

浅紫色表示隐式属性,深紫色表示显式属性

实例对象__proto__属性

原型__proto__

无论是实例对象上的proto还是原型上的proto属性最终的原型都是Object,Object的原型是null,person.proto==Person.prototype

修改原型上属性的两种方式以及它们的不同之处

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

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

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

显而易见,上面这两个栗子只是原型修改一个属性

  • 第二种 稍微变换一下
        Person.prototype.name='sunny';
        function Person() { 
      /*内部是这样子的*/
           //var this={
           //__proto__:Person.prototype
           //}
        var person = new Person();
        Person.prototype={
            name:'cherry'
        }

这个栗子先创建了一个实例对象,然后通过不同的方式修改Person.prototype中的name属性,猜一猜Person.prototype.name和person.name的值是什么?

结果

实例对象和原型(也是一个对象),它们拥有对象的特点——存储的是数据的引用,当两个对象是同一个引用时,修改一个对象的引用值时,不会影响另外一个对象的值。现在person.proto和Person.prototype指向同一个空间,然后Person.prototype换一个空间,而原来的person.proto没有变,所以person.name还是为sunny再举一个更简单的栗子吧~

      var obj = {
            name: 'a'
        };
        var obj1 = obj;
        obj = {
            name: 'b'
        }
结果

现在应该懂了修改引用中的值的妙处了吧~

考一考你

     Person.prototype.name = 'Sunny';
        function Person() {
       /*内部是这样子的*/
           //var this={
           //__proto__:Person.prototype
           //}}
        Person.prototype = {
            name: 'cherry'
        }
        var person = new Person();
结果

想想预编译过程以及函数的执行顺序(什么时候才调用构造函数)就很容易得出结果了~

constructor讲解
construcor可以查看一个对象的构造函数,构造器是一个隐式属性
一个栗子引入

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;
结果

原型链

//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实例对象查找lastname属性时会根据原型链一层层往上找,直到找到为止,或者找不到返回undefined

原型链上的增删改查

原型链上的增删改查和原型的增删改查是差不多的

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

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

这时通过son修改fortune


方法一
方法二

上面两种方式不同,第一种son直接修改person.fortune,很显然,只修改了son.fortune的值,而第二种方式是直接操作fortune引用值,显然是引用值增加一个属性,只是引用值的修改。

补充一个知识

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

第一种var obj = {}是对象自变量的创建形式,上面的两种创建方式是一样的,用var obj={}的方式系统会默认用new Object()的方式创建,平时再构造对象时建议用对象自变量的创建方式,即var obj={}

new的作用
1、创建一个新的对象
2、将构造函数的this指向这个新对象
3、返回这个新对象

Object.create()
创建对象的方法

var obj = Object.create(原型)

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

obj1的原型就是obj,所以obj1继承obj的属性

再举一个例子

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

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

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

        }
     var person=new Person();

的结果是一样的,但是在person的构造函数中定义自己的属性就不一样了

一个错误的概念
全部对象最终都会继承自Object.prototype

正解

对象的原型只能是一个Object或者是null,所以我构造一个没有原型的对象,如下

没有原型

结论
绝大多数对象最终都会继承自Object.prototype,有的对象没有原型

再补充一下
null和undefiend不能调用toString()

因为它们没有原型

call/apply

改变this指向
call

function test(){}
test() //其实执行会默认为test.call()

test()和test.call()没有区别

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

        var obj={}
        Person.call(obj,'cheng',200);//第二,三...是参数

call()就是改变this的指向的,上面的栗子就是让obj使用Person的方法构造对象,这时person构造函数的this为obj,相当于obj.name=name,obj.age=age;

传参
需要把实参按照形参的个数传进去
用法
当两个对象含有相同的属性时,但是另一个对象的属性比较多(功能涵盖)

        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);

上面构造对象的方式显得累赘,我们可以用call的方式使用另一个对象的构造方法

        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);

结论
对象名.call(对象名,参数...)可以实现借用别的对象的属性来构造本对象的属性

apply
使用方式:对象名.(this,[参数])
区别
传参列表不同,call需要把实参按照形参的个数传进去,apply需要传一个arguments

最后看一张图来缕一缕自己的思路吧

function Add() {}
var add=new Add();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容