new做的几件事:
1. 在构造函数中创建一个空对象
2. 函数的this指向空对象,并且将这个对象的隐式原型 (proto)指向构造函数的原型(prototype)
3. 逐行执行代码
4. 隐式返回这个对象
new 实际上是把构造函数原型(prototype)上的属性放在了原型链(proto)上,那么当实例化对象取值时就会在原型链上取,而实例化对象上的prototype已经不见了
继承:通过构造函数创建的对象,可以直接继承该构造函数原型上的属性和方法。
构造函数之间实现继承:A构造函数中有的属性和方法,B构造函数也可以用 (B继承自A、A是B的父类)
1.传统继承方式(类式继承)(原型链继承)
function A(){}
A.prototype.name = "123";//在A的原型上添加属性
function B(){}
B.prototype.age = '18';//在B的原型上添加属性
// 使B的原型指向A的原型
// 让B创建对象能够使用A原型上的属性和方法
//以下三种方法相同
B.prototype = A.prototype;
//B.prototype = new A() //类式继承
// var b = new B();b.__proto__ = A.prototype;
var obj = new B();
console.log(obj.name); // 123
console.log(obj.age); // undefined
// 将自身的该继承的属性和方法都丢掉了
原理:使B的原型指向A的原型;
优点:简单的一行代码,B创建的对象继承了A的原型上的所有属性和方法
缺点:
■ 子类只能继承一个父类(因为继承方式是直接修改子类的prototype,如果再次修改,会将其覆盖)
■ B原型上的方法和属性都被丢弃了,B创建的对象不能继承自身
■ 子类通过prototype继承父类,只能父类单向传递属性给子类,无法向父类传递参数。为什么要向父类传递参数?如果父类中的某属性对参数有依赖关系,此时子类继承父类就需要在 new SuperClass() 时传参
2.借用构造函数继承(构造函数继承)
function A1(name) {
this.name = "111"
}
function A2(arr) {
this.arr = [1, 2, 3]
}
function A3(f) {
this.f = f
}
function A4() {
this.qq = 1529588254;
}
function B(name, arr, f) {
A1.call(this, name);
A2.call(this, arr);
A3.call(this, f);
A4.call(this)
}
var b = new B(111, [2, 6, 9], function() {
console.log(666);
})
console.log(b);
b的打印结果如下
原理:调用其他构造函数里的方法,对自身的数据进行构造
优点:可以继承多个父类的属性,可以动态传递需要的参数来构建数据
缺点:无法继承原型中的属性和方法,无法继承在构造函数外添加的新属性
3.组合继承
// 借用构造函数
function User(name, age, phone, ID) {
this.name = name;
this.age = age;
this.phone = phone;
this.id = ID;
}
User.sayHello = function() {
console.log(`我叫${this.name},我的身份证${this.id}`);
}
// var user = new User('闫振',38,1111,2222);
// 实现VIP与User之间的继承关系 关键步骤1
// 使VIP的原型 指向一个空对象,空对象的隐式原型为User的原型
// 即让VIP的原型的隐式原型指向User的原型
// 原型式继承
VIP.prototype = Object.create(User.prototype);
function VIP(name, age, phone, ID, money) {
// 借用普通用户的构造函数来给自身对象添加属性
User.apply(this, arguments); 关键步骤2
// User(name,age,phone,ID); // this =》 window
// User.call(this,name,age,phone,ID);// this -> {}
this.money = money;
}
VIP.prototype.update = function() {
this.money -= 1000;
console.log('又花了1000大洋');
}
var vipUser = new VIP('闫振', 38, 1111, 2222, 50000000);
// VIP 继承 User
vipUser.sayHello(); // vipUser.__proto__ => Vip.prototype => Object.prototype
原理:调用其他构造函数里的方法,对只身的数据进行构造的同时,通过一个中转对象把自身链接到目标(父级)函数的原型链上。则可以调用父级原型链上的属性和方法。
优点:能够使用父级构造函数原型链上的属性和方法,也能通过父级构建自身的属性
缺点:
4.圣杯模式(组合寄生式继承)
// 将一个构造函数的原型继承另一个构造函数的原型
// 圣杯模式
// 将继承这件事做了封装 封装成一个函数
A.prototype.sayHello = function(){
console.log(this);
}
function A() {}
function B() {}
//原型式继承
// function inherit(son,father){
// // 实现继承
// function F(){};
// F.prototype = father.prototype;
// son.prototype = new F();
// // 将son原本的构造函数给他复原
// son.prototype.constructor = son;
// // 记录son原本继承自谁
// son.prototype.uber = father.prototype;
// }
// inherit(B,A);
// console.log(B.prototype.constructor); // A
// var b = new B();
// b.sayHello();
//封装后放入Function的原型链,方便调用
Function.prototype.inherit = function(Father){
// this -》 调用的对象
function F(){}
F.prototype = Father.prototype;
this.prototype = new F();
// 做一些记录,记录原本的属性应该是什么
this.prototype.constructor = this;
this.prototype.uber = Father.prototype;
}
B.inherit(A);
var b = new B();
b.sayHello();