12-9 你对原型链了解吗?

首先了解构造函数,原型对象,实例对象的关系

每个函数都有 prototype 属性,该属性指向原型对象;
原型对象自带的constructor指回函数;
实例对象的非标准属性__proto__也是指向原型对象(和prototype指向一样)。

原型和原型链
原型好处:所有的实例对象可以共享它的属性和方法;
原型链:它是js继承重要依据,当我们访问对象的属性或方法时,先在对象内部寻找,如果没有就找prototype,在没有,就找prototype的prototype,这样一层一层往上找的关系就形成了原型链。

JS继承方式(6种)

原型链继承

优点:继承父类的属性和方法,也继承父类原型上的方法和属性
缺点:(1) 原型属性在所有实例中是共享的,其中一个实例改变,其他实例也会跟着改变
(2)没办法给父类传参

 // 父类
function Father(){
    this.name = 'baba';
    this.colors = ["red", "blue", "green"]; 
}
Father.prototype.getName = function(){
    return `my name is ${this.name}`
}
// 子类
function Son(age){
    this.age = age
}
// 原型链继承父类
Son.prototype = new Father();  //继承了SuperType的所有属性和方法
// 子类自己添加新方法
Son.prototype.getAge = function (){
    return `my age is ${this.age}`
}

// 实例化对象
var s = new Son(18);
var s2 = new Son(28);

console.log(s.name); //baba
console.log(s2.name); //baba
console.log(s.age)  //18
console.log(s2.age)  //28
console.log(s.getName())  //my name is baba
console.log(s.getAge()) //my age is 18
 
console.log(s.colors)  //[ 'red', 'blue', 'green' ]
s.colors.push('yellow')
console.log(s.colors)  //[ 'red', 'blue', 'green', 'yellow' ]
console.log(s2.colors)  //[ 'red', 'blue', 'green', 'yellow' ]
借助构造函数继承

方法:在子类型构造函数的内部通过使用 apply()和 call()等方法调用超类型构造函数。
优点:
(1) 每个实例不会共享属性,其中一个实例改变属性另一个实例不受影响;
(2) 子类可以向父类传递参数。
缺点:不能继承父类原型上的属性和方法

// 父类
function Father(name){
    this.name = name;
    this.colors = ["red", "blue", "green"]; 
    this.say = function (){
        console.log('gua~gua~gua')
    }
}
Father.prototype.getName = function(){
    return `my name is ${this.name}`
}
// 子类
function Son(name,age){
    Father.call(this,name)  //借助构造函数继承
    this.age = age
}

// 子类自己添加新方法
Son.prototype.getAge = function (){
    return `my age is ${this.age}`
}

// 实例化对象
var s1 = new Son('lily',18);
var s2 = new Son('tom',28);

console.log(s1.name)  //lily
console.log(s1.age)  //18
s1.say()  //gua~gua~gua
console.log(s1.getAge())    //my age is 18
// console.log(s1.getName())   //不能访问父类原型上的属性和方法   s1.getName is not a function


s1.colors.push('yellow')
console.log(s1.colors)  //[ 'red', 'blue', 'green', 'yellow' ]
console.log(s2.colors)   //[ 'red', 'blue', 'green' ]
组合式继承

方法:原型链继承 + 借助构造函数继承 结合
优点:既可以传参又可以复用(属性方法不被共享)
缺点:耗内存(子类构造函数被多次调用)

// 父类
function Father(name){
    this.name = name;
    this.colors = ["red", "blue", "green"]; 
    this.say = function (){
        console.log('gua~gua~gua')
    }
}
Father.prototype.getName = function(){
    return `my name is ${this.name}`
}
// 子类
function Son(name,age){
    Father.call(this,name)  //借助构造函数继承
    this.age = age
}
// 原型链继承
Son.prototype = new Father();
Son.prototype.constructor = Son;    //将 constructor指回 Son

// 子类自己添加新方法
Son.prototype.getAge = function (){
    return `my age is ${this.age}`
}

// 实例化对象
var s1 = new Son('lily',18);
var s2 = new Son('tom',28);

s1.colors.push('black')
console.log(s1.colors)  //[ 'red', 'blue', 'green', 'black' ]
console.log(s2.colors)  //[ 'red', 'blue', 'green' ]
console.log(s1.getName())  //my name is lily
console.log(s2.getName())  //my name is tom

原型式继承

本质是浅拷贝,在es5中,新增了一个函数Object.create()实现了原型式继承
优点: 兼容性好,最简单的对象继;
缺点:
① 多个实例共享属性;
② 无法传参

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"],
    sayname: function () {
        console.log(this.name);
    }
};
var p1 = Object.create(person);
var p2 = Object.create(person, {
    name: {
        value: "Greg"
    }
});
//相当于:
//var anotherPerson =  Object.create(person);
//anotherPerson.name = "Greg";
p2.friends.push("Rob");
p1.friends.push("Barbie");

console.log(person.friends);   //[ 'Shelby', 'Court', 'Van', 'Rob', 'Barbie' ]
p2.sayname();  //"Greg"

寄生式继承

把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回
实例对象不仅具有 person 的所有属性和方法,而且还有自己的 sayName()方法。
缺点:
(1) 在createAnother()中为对象添加函数,不能做到函数复用
(2) 由于是浅复制person对象,pp的改变会影响person对象

//封装继承函数
function createsunny(obj) {
    var clone = Object(obj);  //通过Object()创建一个新对象
    clone.sayName = function() {
         //添加自己的方法
         console.log(this.name);
     };  
     return clone;  //返回这个对象
}  

var person = {
    name: "lily",     
    friends: ["Shel", "Court", "Van"] 
};  
var pp = createsunny(person); 
pp.name='sunny';
pp.friends.push('Barbie');

console.log(person.name); //sunny
console.log(person.friends); // ["Shel", "Court", "Van", "Barbie"]
console.log(pp.name); //sunny
console.log(pp.friends); // ["Shel", "Court", "Van", "Barbie"]
pp.sayName(); //sunny

寄生组合式继承(最理想的继承方式)

通过借用构造函数来继承属性,通过“ 原型链 ”来继承方法,不同的是,这里的“ 原型链 ”本质上是使用寄生式继承来继承超类型的原型
优点:
(1) 既能具有组合继承的优点,又可以不必两次调用超类型的构造函数
(2) 避免了在 SubType.prototype 上面创建不必要的、多余的属性(在原型链继承时,SubType.prototype被重写为SuperType的实例,因此具有了他的实例属性)

function inheritPrototype(subType, superType) {
    var copy= Object(superType.prototype); //创建新对象
    copy.constructor = subType; //增强对象
    subType.prototype = copy; //指定对象 
}

① 通过浅复制创建超类型原型的一个副本(copy)
② 将副本的constructor 属性重新指向subType(在原型链继承中,constructor指向superType)
③ 把副本赋值给subType的原型

// 父类
function Father(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
    this.say = function () {
        console.log('gua~gua~gua')
    }
}
Father.prototype.getName = function () {
    console.log(`father's name is ${this.name}`)
}
// 子类
function Son(name, age) {
    Father.call(this, name)  //借助构造函数继承
    this.age = age
}
inheritPrototype(Son, Father); //调用函数代替原型链继承
// 子类自己的方法
Son.prototype.sayAge = function () {
    console.log(this.age);
}; 

var sub1 = new Son("tom", 29);
var sub2 = new Son("jerry", 27);
sub1.colors.push("black");

console.log(sub1.colors);      //[ 'red', 'blue', 'green', 'black' ]
sub1.getName();          //father's name is tom
sub1.sayAge();           //29  
console.log(sub2.colors);      //[ 'red', 'blue', 'green' ]
sub2.getName();          //father's name is jerry
sub2.sayAge();           //27  

获取原型方法

p.proto
p.constructor.prototype
Object.getPrototypeOf( p)

创建对象方式

字面量
构造函数

new操作符做了什么?
(1)创建一个新对象,在内存中开辟一个空闲的空间
(2)将构造函数的作用域个给新对象(因此this就指向了这个新对象)
(3)执行构造函数中的代码(为这个新对象添加属性)
(4)返回新对象

//函数的名字首字母需要是大写的喔!
function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.say = function () {
        alert(this.name)
    }
}
var person1 = new Person('钟女士', 80, '女');
工厂模式
function person(name, age, gender) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.gender = gender;
    obj.say = function () {
        console.log(this.name)
    }
    return obj;
}
var pp = person('lily',18,'女');
pp.say()
原型模式
function Person() { };
Person.prototype.name = '钟女士';
Person.prototype.age = 80;
Person.prototype.gender = '女';
var person1 = new Person();
console.log(person1)
混合模式(组合使用构造模式和原型模式)
//构造函数模式用于定义实例属性
function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
//原型模式用于定义方法和共享的属性
Person.prototype = {
    constructor :Person,
    sayName:function(){
        console.log(this.name)
    }
}
var person2 = new Person('钟女士', 80, '女');
console.log(person2)


call(),apply(),bind()区别?

① call(),apply()直接执行,bind()需要调用
② call(),bind()可以接受多个参数,apply()接受多个参数需要以数组形式
最后一个问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值