JavaScript进阶系列-原型继承与原型链

先上图bash

原型继承

原型继承实现

function Animal(name){
    // 属性
    this.name = name || 'Animal';
    // 实例方法
    this.sleep = function(){
        console.log(this.name + " 正在睡觉。");
    }
}
    // 原型方法
Animal.prototype.eat = function(food){
    console.log(this.name + " 正在吃 "+food);
}

// 子类
function Tiger(){}
Tiger.prototype = new Animal();
Tiger.prototype.name = "Tiger";

var tiger = new Tiger();
console.log(tiger.name);
console.log(tiger.eat('sleep'));
console.log(tiger.sleep());
console.log(tiger instanceof Animal); //true 
console.log(tiger instanceof Tiger); //true
复制代码

原型链

什么是原型链? 要理解原型链,须要从函数对象constructor、new、Prototype、proto 这五个概念入手。函数

什么是原型链

谈到继承的时候 JavaScript 只有一种结构:对象,每一个对象都有一个 proto 的属性指向他的构造函数的原型对象(是一个对象实例)。该原型对象也有本身的一个原型对象(proto 指向)。ui

函数经过 prototype 属性查找this

组合继承

function Animal(name){
    // 属性
    this.name = name || 'Animal';
    // 实例方法
    this.sleep = function(){
        console.log(this.name + " 正在睡觉。");
    }
}
    // 原型方法
Animal.prototype.eat = function(food){
    console.log(this.name + " 正在吃 "+food);
}

// 子类
function Tiger(value){
    Animal.call(this, value)
}
Tiger.prototype = new Animal();
Tiger.prototype.name = "Tiger";

var tiger = new Tiger();
console.log(tiger.name);

复制代码

class 关键字实现

class Parent {
  constructor(value) {
    this.val = value
  }
  getValue() {
    console.log(this.val)
  }
}
class Child extends Parent {
  constructor(value) {
    super(value)
  }
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
复制代码

函数对象

在 JavaScript 当中 函数即对象。(函数是一等公民) 能够把函数赋值给变量,能够做为参数传递给其余函数,添加属性和调用方法。 凡是由关键字 function 定义的函数都是函数对象,并且,只有函数对象拥有 prototype 属性。指向该函数的原型对象。spa

constructor 构造函数

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        console.log(this.name);
    };
}
复制代码

咱们建立了一个自定义函数 Person() ,构造函数一般使用大写字母开头。prototype

new

要建立 Person() 的新实例,必须使用 new 关键字。 new 调用构造函数实际上会经历如下四个步骤。指针

  • 建立一个新对象
  • 将构造函数的做用域赋值给新对象 (this 指向新的对象)
  • 执行构造函数中的代码
  • 返回新的对象

构造函数和其余普通函数没有什么区别,使用 new 操做符号均可以做为构造函数来调用。code

注意:构造函数若是返回一个对象的话,会覆盖掉新建的对象。cdn

##构造函数的问题对象

建立实例时构造函数每次都要执行

const Tom = new Person('Tom', 12, 'cxy');
const lucy = new Person('lucy', 14, 'dd');
Tom.sayName === lucy.sayName // false
复制代码

这样每一个实例都有一个独立的 sayName 方法。 一般的解决办法是把方法添加到原型链上。

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

Person.prototype.sayName = function (){
    console.log(this.name);
}
复制代码

prototype 原型

就像咱们上面作的那样,使用原型的好处就是可让全部的对象实例共享他所包含的属性和方法。

原型对象

在默认状况下,全部原型对象都会自动得到一个 constructor 属性,这个属性包含一个指向 prototype 属性所在函数的指针。 上面咱们建立的 Person.prototype.constructor 指向 Person .经过这个构造函数咱们还能够给原型对象添加其余的属性和方法。

虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值。 ?? 若是咱们添加的属性和方法和原型对象的属性和方法同样会怎么样

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

Person.prototype.sayName = function (){
    console.log(this.name);
}
Person.prototype.tag = '大V';
const Tom = new Person('Tom', 12, '码农');
Tom.tag = 'dd';
Tom.sayName = function(){console.log(1)};
复制代码

能够看到获取的是咱们后添加的属性和方法,这是读取对象属性和方法的机制决定的。 首先会在原型上查找,原型上没有的话再去原型链查找,一层一层向上查找。

=v= 当咱们修改原型对象会怎么样 嘿嘿

function Person(){}

const Tom = new Person();

Person.prototype = {
    constructor: Person,
    name : "Stone",
    age : 28,
    job : "Software Engineer",
    sayName : function () {
        console.log(this.name);
    }
};

Tom.sayName();   // Uncaught TypeError: Tom.sayName is not a function
复制代码

Tom 出生的时候尚未这些东西,机智如我。

原生对象的原型

原生对象也是经过这种模型建立的。

全部原生引用类型,都在其构造函数的原型上定义了方法。经过上面的例子咱们也能够直接修改原生对象的原型的方法,可是不推荐这种方式,有可能会重写原生方法。

原型链的问题

它省略了为构造函数传递初始化参数这一环节,结果全部实例在默认状况下都将取得相同的属性值。

对于引用类型的共享属性

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["ZhangSan", "LiSi"];
}

Person.prototype = {
    constructor : Person,
    sayName : function(){
        console.log(this.name);
    }
}

const person1 = new Person("dd", 28, "Engineer");
const person2 = new Person("tt", 29, "Teacher");
person2.friends=[]
person1.friends  //  ["ZhangSan", "LiSi"]
复制代码

能够看到共享属性都是备份的。

相关文章
相关标签/搜索