美文网首页
ES5之面向对象

ES5之面向对象

作者: 收纳哥斯拉 | 来源:发表于2019-10-07 12:09 被阅读0次

    1、 请简述一下js原型链

    原型链是Javascript实现类的底层原理,是一种继承机制。说到继承,这起源于JAVAC++的概念。上述两种语言的继承,本质上是一种拷贝行为,将父类的方法拷贝给子类供其使用。

    但是JS中,子类的.prototype对象,通过原型链与父类的.prototype对象形成一条查询管道,如果子类中没有,通过管道返回到父类中寻找,一直回溯到Object.prototype为止。

    比起拷贝继承,这种原型链形式,更加节省内存。另外由于javasctipt的面向对象object oriented OO,实际上叫做object linked other object OLOO更为准确。

    2、 在原型链上Object再往上是什么?

    Object.prototype是原型链的终点了。

    3、 __proto__prototype分别指什么?哪种情况下__proto__prototype的指向是同一个?

    考察的问题:dunder和prototype的指向。通过例子来说明:

    function Workshop(teacher){
       this.teacher = teacher;
    }
    Workshop.prototype.ask = function (question){
       console.log(this.teacher,question);
    }
    
    let deepJS = new Workshop("Kyle");
    let reactJS = new Workshop("Suzy");
    
    deepJS.ask("Is prototype a class?");
    reactJS.ask("Isn't prototype ugly?");
    
    • 解析本段代码,需要了解原型链的布线是什么样的
      image
      子类对象通过__proto__指向父类的对象,红色箭头;函数通过prototype指向同级对象,绿色右箭头。本例子中:
    deepJS.__proto__ == Workshop.prototype;
    

    上面的原型链布线图非常的重要,请多看几遍好好理解

    4、 new生成了一个对象的过程(核心return this)

    • new关键字生成对象分为4个步骤
    1. 生成一个空白新对象this = {}
    2. this绑定目前的execution context
    3. 创建回溯链接,__proto__: Car.prototype
    4. 返回一个新对象出来return this
    var Car = function(loc){
       this = Object.create(Car.prototype);
       this.loc = loc;
       return this;
    }
    Car.prototype.move = function(){
       this.loc++;
    }
    

    new关键字出现的话,下面的代码就可以删除掉。

    new => this = Object.create(yourfunc.prototype);
           return this;
    

    5、 new和Object.create的区别

    newObject.create完成的基本功能是一样的。但是new是与函数配对组合对象下的prototype产生绑定,而Object.create需要指定你要绑定的对象。表现出来的后果就是,new创建的对象享受了函数的constructor属性,也就是所谓的拥有构造函数,其实根本就只是绑定到了function的组合对象上而已这么简单。下面使用实例论证:

    function Car(loc){
       this.loc = loc;
    }
    Car.prototype.move = function(){
       this.loc++;
    }
    function Van(loc){
       Car.call(this,loc);
    }
    var Van.prototype = Object.create(Car.prototype);
    
    let amy = new Van(1);
    console.log(amy.constructor);
    

    amy.constructor指向的不是Van,而是Car,因为Van.prototype没有通过newVan.constructor创建好连接,所以要向上回溯找到具有constructor函数Car.prototype

    • 要注意的思维陷阱:
    1. Car的constructor函数在Car里。不对,在Car.prototype里。
    2. Car.prototype天生就与Car有这Car.prototype.constructor = Car的链接关系。不对,Car.prototype只是默认开辟的一块存储空间,是new创建了这种指向关系。Object.protoype.constructor = object除外,这是顶层设计。

    6、 js的this理解, 如何改变this的指向

    • this的理解

    要判断this的指向,需要明确的是含有this的函数在哪里进行了调用。
    this = the called Object

    • 更改this的指向

    隐性更改的话变换调用的Object即可,显性更改的话使用callapplybind函数override掉the called Object

    • callapplybind
      这三个函数都是Function的原生函数,存储在Function.prototype下面。都是用来复写(override)this的函数,传入的参数是要绑定的对象。call和apply用法基本一致,apply传入参数数组而已。bind则返回一个函数。
      更多参考>>
    object.funcHasThis.call(anotherObject, arg1, arg2);
    object.funcHasThis.apply(anotherObject, ...args);
    const laterExecution = object.funcHasThis.bind(anotherObject);
    laterExecution();
    
    • this会在什么情况下丢失

    混乱情况经常发生在把this作为回调函数的传参时,很难判断到底谁才是真正的调用者。解决方案用call或这bind,经典的this绑定丢失案例:

    function foo(){
       console.log(this.a);
    }
    let obj = {
       a:2,
       foo:foo
    }
    const a = "oops,global";
    setTimeout(obj.foo,100);
    
    //setTimeout的伪代码
    function setTimeout(obj.foo,ms){
       delayfunc(ms);
       //obj.foo只是一个存储地址,里面储存的是foo
       (function foo(){
          console.log(this.a);
        })()
    }
    

    setTimeout内部this指向默认的global,判断发生绑定丢失,解决方案:obj.foo.bind(obj);

    为了方便判断,我总结:出现了{{this}}this.this.的状况会出现绑定丢失。

    7、 js的继承?实现方法?

    箭头函数的用法没有什么好说的,主要比较麻烦的事箭头函数的this指向谁的问题。
    1. 先弄清楚function情况下的this: 遵循隐式绑定,谁调用绑定谁,默认绑定global
    2. 箭头函数比较妙了,绑定所在scope上下文中的对象。

    例子:下面代码中的this等于谁

    function userCreate(name,score){
      this.name = name;
      this.score = score;
    }
    
    userCreate.prototype.increment = function(){
        function add (){
          this.score++;
        }
        add();
    }
    let user1 = new userCreator('Eva',9);
    user1.increment()
    

    this = global

    function userCreate(name,score){
      this.name = name;
      this.score = score;
    }
    
    userCreate.prototype.increment = function(){
        const add =()=>{
          this.score++;
        }
        add();
    }
    let user1 = new userCreator('Eva',9);
    user1.increment()
    

    this = user1, increment调用时,开辟的execution context中,this=user1,箭头函数绑定的不是它的调用,而是在execution context中的this值。

    如果不了解函数的执行过程,包括execution context,scope等概念,理解箭头函数的绑定会比较的混乱,推荐https://medium.com/@js_tut/execution-context-the-call-stack-d1fbe34f6fe9补习一下概念。

    • 那么箭头函数怎么用?
      箭头函数日常的用法跟ES5没有什么不一样。但是在对象生成的时候尽量不要使用箭头函数,使用统一的ES5标准,不然自己会被自己搞晕。但在对象中有一种情况要使用箭头函数,但就是在写子方法的时候:如上述的第二部分的代码。

    8、 js的继承?实现方法?

    Js的继承说到底,是委托查找。经典的实现方法是原型类,变体可以使用委托设计模式,ES6中加了class语法糖,但是本质是别人已经把背后特别丑的原型类包了起来而已。个人习惯使用ES5的语法,虽然丑但调试起来比较方便,另外我觉得这比较符合JavaScript的DNA,ES6这个语法在我看来是一场粉饰太平的迎合。个人观点仅供参考。

    • 原型类
    function Workshop(teacher){
       this.teacher = teacher;
    }
    Workshop.prototype.ask = function (question){
       console.log(`${this.teacher},ask: ${question}`);
    }
    //继承部分
    function AnotherWork(teacher){
       Workshop.call(this,teacher);
    }
    AnotherWork.prototype = Object.create(Worksop.prototype);
    //多态
    AnotherWork.prototype.speak = function(question){
       this.ask(question.toUpperCase());
    }
    
    • ES6语法糖
    class Workshop{
        constructor(teacher) {
             this.teacher = teacher;
        }
        ask(question){
            console.log(`${this.teacher},ask: ${question}`);
        }
    }
    //继承部分
    class AnotherWork extends Workshop{
        constructor(teacher){
            super(teacher);
        }
        speak(question){
            this.ask(question.toUpperCase());
        }
    }
    
    • 委托类
    var Workshop{
       setTeacher(teacher){
           this.teacher = teacher;
       },
       ask(question){
           console.log(`${this.teacher},ask: ${question}`);
       }
    }
    //将Workshop assign 给 AnotherWork
    var AnotherWork = Object.assign(
        Object.create(Workshop),
        {
       speak(question){
       this.ask(question.toUpperCase());
      }
    });
    //应用
    let deepJS = Object.create(AnotherWork);
    deepJS.setTeacher("Kyle");
    deepJS.speak("Hello oloo?");
    

    委托类的效率和灵活性是要比继承类都好的,但是继承类的可读性更好,在项目维护的时候,可读性要高于效率。

    相关文章

      网友评论

          本文标题:ES5之面向对象

          本文链接:https://www.haomeiwen.com/subject/rzfmpctx.html