ES5中的类与继承 你真的会吗?

吐槽君 分类:javascript

课本

面向对象:彻底弄懂ES5中的类与继承

试题

原型链卷

  1. 介绍一下JavaScript中的原型链

  2. 属性搜索,请说明下面代码的执行过程和结果。

    function SuperType(){
        this.property = true;
    }
    
    SuperType.prototype.getSuperValue = function(){
        return this.property;
    }
    
    function SubType(){
        this.subproperty = false;
    }
     
    SubType.prototype = new SuperType(); 
    
    SubType.prototype.getSubValue = function(){
        return this.subproperty;
    }
    
    let instance = new SubType(); 
    
    console.log(instance.getSuperValue());
     
  3. instanceof 可以做什么?原理是?

类卷

  1. JavaScriptnew 做了什么?

  2. ES5怎么实现类?

继承卷

  1. ES5怎么实现继承?
  2. ES6里,子类的构造函数中的super 是干什么用的?

解析

1.介绍一下JavaScript中的原型链

思路:概念题,背概念(3句话)+个人理解。

回答:在JavaScript中,每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部属性指向构造函数的原型对象(概念)。这样,每个实例就可以共享构造函数原型对象上定义的方法或属性(理解)。

追问:函数、对象的原型。

2.属性搜索,请说明下面代码的执行过程和结果。

思路:考察对象原型链上的属性搜索。

回答:对象的属性搜索其实就是在一个对象中找属性。刚开始这个对象是实例,如果没找到,就沿着内部属性,在原型对象上找,还没找到,就在原型对象的原型对象找,直到某一层的原型对象为null

这段代码首先会沿着原型链,找到SuperType原型对象上的getSuperValue方法。该方法会被instance当作对象方法执行,所以this指向instance。开始下一轮搜索,然后在SubType的原型对象上找到了property。最终,输出true.

追问:如果SubType构造函数的subproperty改为property,代码会输出啥?

3.instanceof 可以做什么?原理是?

思路:intanceof的概念。

回答:instanceof用来检测对象的原型链上是否出现构造函数的prototype。原理就是取对象的原型指针所指原型对象与构造函数的原型对象比较,如果相等,返回true,不相等,则取原型对象的原型指针所指的原型对象再次比较。一直取到所指的原型对象为空,则返回false。

追问:Talk is cheap,show me code!

4.JavaScriptnew 做了什么?

思路:new的概念(5步),背就完事了。

回答:

1.  创建一个空对象。
2.  指定空对象的`__proto__`为构造函数的`prototype`。
3.  绑定该空对象到构造函数。
4.  执行绑定后的构造函数。
5.  如果构造函数有返回值,则返回返回值,否则返回对象。
 

追问:为什么要有第 2 步?

5.ES5怎么实现类?

思路:三种实现。工厂、构造函数、原型。讲清楚它们的原理,有啥缺陷。

回答:EcmaScript里的对象,其实就是给一个空对象加上各种属性和方法,所以把添加的步骤抽象成一个函数,这就是工厂模式。工厂模式最大的问题,就是没有好好利用原型,后续没法继承。构造函数,出现了,它使用构造函数使用this来增强对象,而new操作符则自动绑定原型。而原型就是在构造函数模型基础上,将公有的属性和方法绑定到构造函数原型上,而不是实例上。

追问:在原型模式里,哪些属性方法放到构造函数里,哪些放到构造函数的原型上?

6.ES5怎么实现继承?

思路:直接背组合继承和寄生组合式模式。

回答:Talk is not cheap,show you talk and code !

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log(this.name);
}
function Student(name,sex){
    Person.call(this,name);  // 关键代码1
    this.sex = sex;
}
Student.prototype = new Person();  // 关键代码2
Student.prototype.saySex = function(){
    console.log(this.sex)
}
 

关键代码1:这行代码盗用Person构造函数,原本属于Person的属性,会被定义在Student实例里。

关键代码2:这一行将会导致Student实例的__proto__指向一个Person实例,而Person实例的__proto__指向Person原型对象。这样Student就可以通过原型链,访问到Person上的属性和方法啦!

寄生组合式模式是为了简化关键代码2的。因为,我们只是希望获得一个__proto__指向Person.prototype对象,而new操作的前两步就以及可以实现了,所以,有如下代码:

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log(this.name);
}
function Student(name,sex){
    Person.call(this,name);   				// 1
    this.sex = sex;           				// 2
}
// 改变开始
Student.prototype = function(){      		// 3
    let proto = {};
    proto.__proto__ = Person.prototype;
    proto.constructor = Student;
    return proto;
}(); 
// 改变结束
Student.prototype.saySex = function(){   	// 4
    console.log(this.sex);
}
 

当然,这里的改变可以抽象成一个函数。

function solution(superType,subType){
    let proto = Object.create(superType.prototype);
    proto.constructor = subType;
    subType.prototype = proto;
}
solution(Person, Student);
 

追问:代码 1和2,3和4 可以交换顺序吗?为什么?

7.ES6里,子类的构造函数中的super 是干什么用的?

思路:有些超纲,但是,可以分析分析。

回答:ES6的继承也还是通过原型链实现的,而super出现在构造函数中,所以super指向父类的构造函数。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

没有追问。

追问题

1.追问

回答:函数是Fuction的实例,所以,函数除了拥有原型对象外,还有一个内部属性指向Function的原型对象。而对象是Object的实例,所以,对象会有一个内部属性指向Object的原型。

结果:理解到位。

2.追问

回答:根据this绑定原则,getSuperValuethis会绑定在instance上,所以property的搜索会从instance开始,输出false

结果:理解到位。

3.追问

回答:没问题!

function myInstanceOf(obj,cstc){
    if(typeof objProto !== "object"){
        throw '第一个参数得是一个对象';
    }
    if(typeof ctsc !== "function"){
        throw '第二个参数得是一个函数';
    }
    let objProto = obj.__proto__;
    let cstcProto = cstc.prototype;
    while(true){
        if(objProto === null){
            return false;
        }
        if(objProto === cstcProto){
            return true;
        }
        objProto = objProto.__proto__;
    }
}
 

结果:思路清晰,有对输入做异常处理,不错。

4.追问

回答:为了形成原型链,用上原型上方法和变量。当然,如果不在原型上定义方法和变量,那就……

结果:看来不止会背概念。

5.追问

回答:这两者的区别就是,对于同一构造函数创建的实例,前者是私密的,后者是公共的。

结果:到位。

6.追问

回答:不可以。如果1和2交换了顺序,如果父子有相同属性,那么子类的属性会被覆盖掉。3和4也不能交换顺序,应为3会改变子类原型指向,4定义的方法会丢失。

结果:标准答案。


创作不易!如果对你有帮助,还请点赞收藏。
如果有疑惑可以在下方留言。
如果有什么建议,可以私信我。

回复

我来回复
  • 暂无回复内容