原型 原型链 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();

你可能感兴趣的:(原型 原型链 call/apply)