文章目录
首先了解构造函数,原型对象,实例对象的关系
每个函数都有 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()接受多个参数需要以数组形式
最后一个问题