ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
2.2.1 call()
调用这个函数, 并且修改函数运行时的 this 指向
fun.call(thisArg, arg1, arg2, ...)
(1) thisArg :当前调用函数 this 的指向对象
(2) arg1,arg2:传递的其他参数
练习:10-call方法
<script>
// call 方法
function fn(x, y) {
console.log('我想喝手磨咖啡');
console.log(this);
console.log(x + y);
}
var o = {
name: 'andy'
};
// fn();
// 1. call() 可以调用函数
// fn.call();
// 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
fn.call(o, 1, 2);
</script>
2.2.2 借用构造函数继承父类型属性
核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
// 父类
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 子类
function Student(name, age, sex, score) {
Person.call(this, name, age, sex); // 此时父类的 this 指向子类的 this,同时调用这个函数
this.score = score;
}
var s1 = new Student('zs', 18, '男', 100);
console.dir(s1);
练习:11-借用父构造函数继承属性
<script>
// 借用父构造函数继承属性
// 核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
// 1. 父构造函数
function Father(uname, age) {
//this 指向父构造函数的实例
this.uname = uname;
this.age = age;
}
//2.子构造函数
function Son(uname, age,score) {
//this 指向子构造函数的实例
// 核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
Father.call(this,uname,age);
this.score = score;
}
var son = new Son("刘德华",18,100);
console.log(son);
</script>
2.2.3 借用原型对象继承父类型方法
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
核心原理:
① 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
② 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象
③ 将子类的 constructor 从新指向子类的构造函数
练习:12-借用原型对象继承方法
<script>
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {
//this 指向父构造函数的实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function(){
console.log(10000);
}
//2.子构造函数
function Son(uname, age,score) {
//this 指向子构造函数的实例
// 核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
Father.call(this,uname,age);
this.score = score;
}
// Son.prototype = Father.prototype; //这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起改变
Son.prototype = new Father();
//如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son;
//子构造函数专门的方法
Son.prototype.exam = function(){
console.log('孩子要考试');
}
var son = new Son("刘德华",18,100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor)
</script>
</body>
2.2.3.类的本质
- class本质还是function.
- 类的所有方法都定义在类的prototype属性上
- 类创建的实例,里面也有__proto__ 指向类的prototype原型对象
4.所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
5.所以ES6的类其实就是语法糖. - 语法糖:语法糖就是一种便捷写法. 简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,
那么这个方法就是语法糖
练习:13-ES6类的本质
<script>
// ES6 之前通过 构造函数+ 原型实现面向对象 编程
// (1) 构造函数有原型对象prototype
// (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身
// (3) 构造函数可以通过原型对象添加方法
// (4) 构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
// ES6 通过 类 实现面向对象编程
class Star {
}
console.log(typeof Star);
//1.类的本质其实还是一个函数,我们也可以简单的认为类就是构造函数的另一种写法
// (1) 构造函数有原型对象prototype
console.log(Star.prototype);
// (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身
console.log(Star.prototype.constructor);
// (3) 构造函数可以通过原型对象添加方法
Star.prototype.sing = function () {
console.log('冰雨');
}
// (4) 构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
var ldh = new Star();
console.dir(ldh);
console.dir(ldh.__proto__ === Star.prototype);
</script>