美文网首页web前端开发求职面试攻略
常见前端开发面试题(面向对象部分)

常见前端开发面试题(面向对象部分)

作者: 党云龙 | 来源:发表于2019-11-12 16:09 被阅读0次

    new关键字的作用


    1.new会在内存中创建一个新的空对象
    2.new会让this指向这个新的对象
    3.实例化构造函数里面的代码 目的:给这个新对象加属性和方法
    4.new会返回这个新对象 (所以构造函数里面不需要return)

    原型链


    所谓的原型链,就是指prototype。

    简单来说,我们实例化了一个对象,你实例的这个对象肯定需要有一个东西指向它的原型吧,这个东西就是prototype,prototype是每个函数都有的,如果是对象,需要用proto

    还记得面向对象的最基本思路吗:属性私有,方法公用。
    当我们创建一个构造函数的时候,构造函数里面只应该有属性,方法应该全部定义到prototype上,这跟js的内存使用有关系,当我们实例化一个对象的时候,你new的构造函数中的值,都会再内存中复制一个并占用空间,但是prototype中的方法,只有一个引用,并不会占用空间。这就是prototype的作用。

    prototype

    prototype其实是一个对象。
    你打印它的时候会发现,它里面的constructor其实就是你的原型。

    举个例子:

    // 创建一个原型对象 这个方法也叫做构造函数
    // 构造函数的目的是为了解决当你有多个相似对象的使用,可以创建一个公用的源模型,然后从这个基础之上再开发
    var model = function(canshu){
        this.num1 = canshu;
        //你可以把方法定义到这个里面,但是这个方法属于每个实例的私有方法,它是可以被更改的。
        this.fun = function(){
            console.log(canshu);
        }
    }
    //console.log(model.num1); //undefined 你不能直接打印原型对象的值
    
    // 所谓的原型链就是给源模型定义方法的地方,这个方法是公共方法,每个被实例的原型都可以拥有
    // 它跟php中public和static是一样的
    model.prototype.publicFun = function(){
        console.log(this.num1+1); //2
    }
    
    var demo = new model(1);
    console.log(demo.num1); //0
    demo.publicFun(); //2
    

    继承


    所谓的继承,就是我有俩class,或者你可以理解为构造函数,现在我需要用后面这个获取前面那个的一些属性和方法。

    其实我只需要在第二个里面修改一下this指针的指向就行了。

    var class1 = function (){
        this.name = 25;
    }
    class1.prototype.fun1 = function(){
        console.log("我是class1上的方法");
    }
    
    //开始使用class2继承class1
    var class2 = function (age){
        //在es5中的继承 其实就是在构造函数内部改变了指针的位置
        //但是这样只能继承属性,如果你想继承方法请往下看
        class1.call(this,name);
        this.age = age
    }
    
    //继承class1中的方法 
    //class1.prototype = class2.prototype 这样并不友好,我们创建一个空方法复制并获取class1
    var f = function(){}
    f.prototype = class1.prototype
    class2.prototype = new f()
    
    var sl = new class2(11);
    console.log(sl.name);
    sl.fun1();
    

    这里需要注意以下,当你需要复制prototype上面方法的时候,需要创建一个空方法再复制。

    拷贝对象


    这里有一个误区,就是如果我让你把a里面的值,赋值给b,你会怎么做,是:

    var b = a
    

    这样吗,如果你这样写,你就上当了。这样只会把b的this指针指向a,但是并没有真的复制一个对象。

    你应该这样做:

    //深复制对象方法    
    var cloneObj = function (obj) {  
        var newObj = {};  
        if (obj instanceof Array) {  
            newObj = [];  
        }  
        for (var key in obj) {  
            var val = obj[key];  
            //newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; //arguments.callee 在哪一个函数中运行,它就代表哪个函数, 一般用在匿名函数中。  
            newObj[key] = typeof val === 'object' ? cloneObj(val): val;  
        }  
        return newObj;  
    };  
    //测试    
    var obj = {
        a:function(){
            console.log(this.b.c)
        },
        b:{c:1},//设置一个对象  
    },
    newObj = cloneObj(obj);//复制对象  
    newObj.b.c=2;//给新对象赋新值  
    obj.a();//1,不受影响  
    newObj.a();//2  
    

    使用for in去遍历一下要clone的对象,然后再重新赋值。

    这里有人曾经提出了一个深拷贝和浅拷贝的问题,

    //prototype是给方法 设置属性的地方
    obj.a.prototype.a = function(){
        console.log(123);
    };
    //obj.a.prototype.a(); //123
    newObj = cloneObj(obj);//复制对象  
    //newObj.a.prototype.a(); //123 深拷贝可以实现对prototype的拷贝
    

    我认为的深拷贝是能够把再prototype上的内容也拷贝到新得对象上得拷贝,可见通过for in是可以实现的。

    闭包


    我们不想每次都声明一大堆全局变量—首先大量的全局变量不符合模块化精神,也容易造成变量污染。

    那么我们能不能把值保存在函数体内部呢?(你也可以理解为用局部变量去储存值,用的时候从局部变量去读取)

    必然可以!不过,我们需要一个函数来连接函数体内部的值,这个方法就叫闭包。(我过去一度被闭包这俩问题所迷惑,现在看来它就是一个函数,类似于你操作vuex中的方法)

    请看例子:

    var nums = {
        num:0,
        add:function(){
            return this.num+=1;
        }
    }
    console.log(nums.add());//1
    console.log(nums.add());//2
    
    
    console.log(nums.num);//2
    

    当我们需要使用的时候,读取一下即可,修改的时候,执行我们闭包中的方法就ok了。
    面试考这个的时候,就是为了看你对vuex和redux的理解。

    apply/call和bind函数


    其实这三个函数都是一样的作用,用于修改函数体内部this指针的指向。

    你最好别去看官方的解释,你没做过项目的话肯定会一头雾水。

    我们直接看例子:

    添加商品的例子

    比如我想做一个上架商品加减个数的效果。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>apply和call</title>
        </head>
        <body>
            <div>上架商品</div>
            <div>香蕉:<span id="mynum">0</span>个</div>
            <button onclick="shangpin.add()">+</button>
            <button onclick="shangpin.del()">-</button>
            
            <script type="text/javascript">
                var shangpin = {
                    pinzhong:{
                        xiangjiao:{
                            num:0
                        }
                    },
                    suanfa:{
                        add:function(){
                            return this.num+=1;
                        },
                        del:function(){
                            return this.num-=1;
                        }
                    },
                    dom:function(){
                        document.getElementById("mynum").innerHTML = this.pinzhong.xiangjiao.num;
                    },
                    add:function(){
                        //apply方法,可以把当前对象的this指针,修改为指向其他对象
                        //目的就是为了调用方法,让数据和逻辑分离
                        //apply[thisobj,[参数1,参数2]] //如果你需要传递参数,写到后面的array中
                        //apply call和bind作用完全一样,区别就是bind返回一个函数,需要再调用一次
                        this.suanfa.add.apply(shangpin.pinzhong.xiangjiao);
                        this.dom();
                    },
                    del:function(){
                        this.suanfa.del.apply(shangpin.pinzhong.xiangjiao);
                        this.dom();
                    }
                }
            </script>
        </body>
    </html>
    

    我们用实现mvc的方式把数据和逻辑分开,你会发现,这里的suanfa里面,因为es5函数作用域的关系,他并不指他本身,他指的是shangpin这个大的父级类,所以我们需要把的定义到父级的数据层中去。

    这里就需要修改this指针的了。

    于是就用到了apply和call。当然你用bind也是一样的作用。只不过bind返回一个函数,你还得再后面再执行一下才行。

    相关文章

      网友评论

        本文标题:常见前端开发面试题(面向对象部分)

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