面试题

1、编写plus和minus实现如下需求
let n = 10;
let m = n.plus(10).minus(5);
console.log(m);
此题写法:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题</title>
</head>

<body>
    <script>
        // // 往所属的内置类Number的原型上扩展plus和minus方法
        // 写法1、Number.prototype.plus = function plus(num) { //函数表达式方法,将匿名函数改为具名函数,在内部可以调用,但在全局不行,有益于以后使用递归,不会与全局冲突。
        //     // =>this指向的是我们要操作的那个数字实例(对象)
        //     // 不管是否是数字,强制转换为数字类型
        //     num = Number(num);
        //     if (isNaN(num)) {
        //         num = 0;
        //     }
        //     // console.log(this); //Number__proto__: Number[[PrimitiveValue]]: 10 原始值是10,this是一个对象
        //     //new Number(10)+10 = 20是可用的
        //     // 返回Number的实例,实现链式写法
        //     return this + num;
        // }
        // Number.prototype.minus = function minus(num) {
        //     // 不管是否是数字,强制转换为数字类型
        //     num = Number(num);
        //     if (isNaN(num)) {
        //         num = 0;
        //     }
        //     return this - num;
        // }

        // //写法2、 以上代码是可行的,但是判断num的代码是相同的,所以可以提取出来
        // const checkNum = function checkNum(num) {
        //     // 不管是否是数字,强制转换为数字类型
        //     num = Number(num);
        //     if (isNaN(num)) {
        //         num = 0;
        //     }
        //     return num;
        // }
        // // 提取后,在方法中调用
        // Number.prototype.plus = function plus(num) {
        //     return this + checkNum(num);
        // }
        // Number.prototype.minus = function minus(num) {
        //     return this - checkNum(num);
        // }

        // 若像写法2中所写的,全局的方法checkNum,可能会和其他方法冲突,所以可以使用闭包~function(){}()包起来
        // 方法3
        ~ function anonymous(proto) {
            const checkNum = function checkNum(num) { //此时的这个方法是属于私有的,不会与全局的冲突
                // 不管是否是数字,强制转换为数字类型
                num = Number(num);
                if (isNaN(num)) {
                    num = 0;
                }
                return num;
            }

            // Number.prototype.plus = function plus(num) {
            //     return this + checkNum(num);
            // }
            // Number.prototype.minus = function minus(num) {
            //     return this - checkNum(num);
            // } //Number.prototype使用多次,可以作为参数传进方法中 写法如下=>
            proto.plus = function plus(num) {
                return this + checkNum(num);
            }
            proto.minus = function minus(num) {
                return this - checkNum(num);
            }
        }(Number.prototype)

        let n = 10;
        // n.plus(10); //为打印this而存在
        let m = n.plus(10).minus(5);
        console.log(m); //15 (10+10)-5

        // "+" 左右其中一方有引号是字符串,就会进行字符串拼接
        // Number的实例对象中,会先找valueOf找初始值,如(new Number(10)).valueOf(),找不到会toString() ,再进行运算


        /*
            创建一个数据类型值:(不论哪一种方式,创建出来的结果都是所属类的实例)
                1、字面量方式
                2、构造函数方式
        */
        // 基本数据类型两种创建方式是不一样的,字面量创建的是基本类型值, 构造函数创建的是引用类型值。
        // let a = 10;
        // let b = new Number(10);
        // console.log(b.valueOf() === a) //对象结果的原始值是基本类型数字10
    </script>
</body>
</html>

2、写出下列代码运行结果,分析运行思路

function fun() {
            this.a = 0;
            this.b = function () {
                alert(this.a);
            }
        }
        fun.prototype = {
            b: function () {
                this.a = 20;
                alert(this.a);
            },
            c: function () {
                this.a = 30;
                alert(this.a);
            }
        }
        var my_fun = new fun();
        my_fun.b(); //'0'
        my_fun.c(); //'30'

运行思路如下代码中注释所示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题2-重置类的原型指向</title>
</head>

<body>
    <script>
        // 2、画图计算下面的结果

        /*
            运行以下代码会创建一个堆,就是fun函数(堆) 里面的内容:
                函数:代码字符串:“this.a = 0;this.b = function () {alert(this.a);}”
                对象:键值对: name : fun,
                            prototype:fun.prototype(对象,也是个堆)
                            __pproto__:每个对象都有这个属性,是所属类的原型,是个函数,所以是Function.prototype

            fun.prototype:是个对象,可称为堆AAAFFF111,内容为:
                constructor:fun
                __pproto__:因为是对象,没有new,不是哪个的实例,所以指向Object.prototype
        */
        function fun() {
            this.a = 0;
            this.b = function () {
                alert(this.a);
            }
        }
        /*
            fun.prototype = {...}这是自定义的对象,先执行"="右边的,因为等号优先级是右边优先,这个自定义对象的堆内容为:
                b:function(){}..,
                c:function(){}...
                __proto__:不是某一个的实例,所以指向Object.prototype
            在此时,赋值的时候,是在重置类的原型指向,fun.prototype不再指向上文中的AAAFFF111,而是此时的自定义对象,可称为AAAFFF222.
            重置类的原型指向,带来的问题1:构造函数可能会丢失
            为避免出现问题,在堆AAAfff222中需要手动设置构造函数,为constuctor:fun    
        */
        fun.prototype = {
            // constuctor:fun,   构造函数可能会丢失,需要手动设置
            b: function () {
                this.a = 20;
                alert(this.a);
            },
            c: function () {
                this.a = 30;
                alert(this.a);
            }
        }

        /*
            var my_fun = new fun();创建一个新的对象,堆可以称为AAAFFF333,是fun的实例,this指向my_fun内容为:
                this.a = 0; => a : 0,
                this.b = function(){alert(this.a)}
                __proto__:指向所属类的原型,即fun.prototype,也就是AAAfff222 

        */
        var my_fun = new fun();
        /*
          my_fun.b(); => 私有函数b,  
          alert(this.a) => this指向my_fun, my_fun.a = 0;所以alert(),是字符串0=> '0'
        */
        my_fun.b(); //'0'
        /*
         my_fun.c();=> my_fun中没有c方法,所以是向上级查找=> 共有函数c(){
             this.a = 30;
             alert(thisa)
         } ,
         =>this指向my_fun,所以,在此是修改堆AAAFFF333中a的值,  
          this.a = 30; => my_fun.a =30; =>a = 30;
          alert(this.a); //'30'
        */
        my_fun.c(); //'30'
    </script>
</body>
</html>

3、写出下面代码执行输出的结果

function C1(name) {
            if (name) {
                this.name = name;
            }
        }
        function C2(name) {
            this.name = name;
        }
        function C3(name) {
            this.name = name || 'join';
        }
        C1.prototype.name = 'Tom';
        C2.prototype.name = 'Tom';
        C3.prototype.name = 'Tom';
        alert((new C1().name) + (new C2().name) + (new C3().name)); //'Tomundefinedjoin'

此题的思路在以下代码中:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题3-写出下面代码执行输出的结果</title>
</head>
<body>
    <script>
        function C1(name) {
            // =》name是undefined,没有给实例设置私有的属性name,会向__proto__原型上找,找到Tom
            if (name) { //条件不成立
                this.name = name;
            }
        }
        function C2(name) {
            this.name = name;
            // 给实例设置私有属性name,不过值是undefined,=> this.name = undefined
        }
        function C3(name) {
            this.name = name || 'join';
            // 给实例设置私有属性name,不过值是undefined,=> this.name = undefined || 'join' ,左边为假,返回join
        }
        C1.prototype.name = 'Tom'; //所属类原型中公有属性name为Tom
        C2.prototype.name = 'Tom';//所属类原型中公有属性name为Tom
        C3.prototype.name = 'Tom';//所属类原型中公有属性name为Tom
        alert((new C1().name) + (new C2().name) + (new C3().name));//'Tomundefinedjoin'
        /*
            (new C1().name) => 没有传参,name是undefined,里面的判断不成立,没有设置私有属性name => 会找原型上的'Tom';
            (new C2().name) => 找私有属性name => undefined
            (new C3().name) => 找私有属性name => 'join'
            所以最后结果:
                => 'Tomundefinedjoin'
        */
    </script>
</body>
</html>

4、写出以下代码执行结果

function Foo() {
            getName = function () {
                console.log(1);
            };
            return this;
        }
        Foo.getName = function () {
            console.log(2);
        };
        Foo.prototype.getName = function () {
            console.log(3);
        };
        var getName = function () {
            console.log(4);
        };
        function getName() {
            console.log(5);
        };
        Foo.getName();
        getName();
        Foo().getName();
        getName();
        new Foo.getName();
        new Foo().getName();
        new new Foo().getName();

上述代码中,会涉及到运算符优先级,详见网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence;上述代码运行涉及思路:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题4-运算符优先级</title>
</head>

<body>
    <script>
        /*
            ECStack,执行环境栈:
                EC(G):
                    VO(G):
                        变量提升:
                            function Foo(){...}... => Foo = 函数在变量提升时已经声明且赋值了,所以在此创建一个变量Foo,创建一个堆,称为AAAFFF000,并将这个堆赋值给Foo,AAAFFF000堆里面的内容为:
                                    函数:代码字符串:
                                            'getName=function(){console.log(1);};return this;'
                                    对象:键值对:
                                            name:Foo, 
                                            prototype:Foo.prototype,
                                            __proto__:Function.prototype,
                                            getName = function(){...} => 2
                                Foo的原型:Foo.prototype: 对象,可以称为堆BBBFFF000,内容为:
                                    constructor:Foo,
                                    __proto__: Object.prototype,
                                    getName = function(){...} => 3; 

                            var getName = function(){...} => 创建变量getName;在此时并未赋值;
                            function getName = function(){console.log(5)}; => 已经有定义过的getName,不需要重新定义,直接给全局的getName赋值,为一个函数,此时全局的getName的值为堆AAAFFF111,执行的话是输出5.

                        代码执行:
                            Foo.getName = function (){console.log(2)} => 把Foo作为一个对象,在堆AAAFFF000中添加一个方法,getName=function(){console.log(2)} => 输出2
                            Foo.prototype.getName = function(){console.log(3)} => 此代码是在Foo的原型中,添加一个方法,getName,即在堆BBBFFF000中添加一个方法getNAme=function(){console.log(3)};
                            var getName = function(){console.log(4)}=> var getName在变量提升阶段已经操作过一次,但是没有赋值,在此时要给全局的getName重新赋值,为堆AAAFFF222,输出的值为4,是输出4的函数。
                            
                            Foo.getName(); => 把Foo当做普通对象来走,调用Foo中的getName方法,是私有的,直接输出2 ;

                            getName(); => 全局的getName,输出为4,也就是AAAFFF222的值;

                            Foo().getName(); => 即先执行Foo(),然后执行”结果.getName()“; Foo()中,没".",this是window,作为函数来说,Foo中,getName不是自己私有的,所以要向上级作用域找,是全局的,将全局的getName的值改为堆AAAFFF333,输出的值为1,是输出1的函数。然后return this,就是return window,所以window.getName()是找的全局的,所以输出结果为1

                            getName() => 全局的getName,所以值为1
                            
                            new Foo.getName(); => 根据运算符优先级,new (无参数列表)的优先级是18,而Foo.getName,成员访问(带点的)的优先级为19,所以根据优先级的大小,应该先执行Foo.getName,然后执行new (Foo.getName)(), Foo.getName函数的值是2,new(Foo.getName)(),创建实例的时候会将这个函数执行,所以值为2

                            new Foo().getName(); => 根据运算符优先级,new (带参数列表)是19,成员访问也是19,所以是从左到右执行,所以会先new Foo(),new Foo()是创建Foo类的实例,然后看实例.getName(),先看自己里面有没有getName,没有的话通过原型链去上级查找,在Foo()中没有this.getName=...,所以实例中的Foo()是没有这个私有属性,因为实例中没有私有的getName属性,所以找原型上的方法,就是Foo.prototype.getName,输出为3。

                            new new Foo().getName(); => 与typeof typeof []一样,是先算tyoeof [],然后将这个结果再算 typeof 右边的结果,
                                所以这个是先new Foo(),因为带"()"了是有参数列表,是19,而".getName()"成员访问优先级也是19 ,所以从左向右执行。
                            => new new Foo().getName(); 所以先执行new Foo(),创建一个Foo()的实例;
                            -> new 实例.getName(); 因为实例,后面是没有参数列表,运算值是18,没有后面的"."成员访问(优先级值为19)的优先级高,所以执行实例.getName,但是因为在实例中没有私有属性getName,所以通过原型链向上级查找,就是原型上的getName;
                            -> new 原型上的getName(); 就是new function () {console.log(3);} ,执行把它当做普通函数执行,并且返回这个函数的实例,无什么用,但是会执行此函数,所以输出为3.

        */
        function Foo() {
            getName = function () {
                console.log(1);
            };
            return this;
        }
        Foo.getName = function () {
            console.log(2);
        };
        Foo.prototype.getName = function () {
            console.log(3);
        };
        var getName = function () {
            console.log(4);
        };
        function getName() {
            console.log(5);
        };
        Foo.getName();
        getName();
        Foo().getName();
        getName();
        new Foo.getName();
        new Foo().getName();
        new new Foo().getName();
        // 运算符优先级:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
    </script>
</body>
</html>

5、a等于什么值,会让下面条件成立

var a = ? ;
 if (a == 1 && a == 2 && a == 3) {
            console.log('OK');
        }

此题的解法如下代码所示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题5-面向对象的新玩法</title>
</head>

<body>
    <script>
        /*
        面试题5:a等于什么值,会让下面条件成立
            var a = ?;
            if (a == 1 && a == 2 && a == 3) {
                console.log('OK');
            }
        */
        /*
            == 相等 与 === 绝对相等 对比
            1、左右数据值类型不同,三个等号不能转换数据类型,直接返回false,但是两个等于号会默认先转换为一致的数据类型在进行比较。

            NaN==NaN:NaN和谁都不相等(包括自己)。
            null==undefined:null和undefined两个等号比较是相等的(三个则不相等),但是他们和其他任何值都不相等。
            对象==字符串:把对象转换为字符串(对象.toString())。
            剩余的情况都是转换为数字(对象转换为数字:Number(对象.toString());字符串转数字:Number(字符串))。

        */
        /*
            方案一:利用比较的时候默认会转化为字符串的机制,通过重写toString()来完成需求
        */
        /*var a = {
            i: 0,
            toString() {
                return ++this.i;
            }
        };
        // => a==1:a.toString()
        if (a == 1 && a == 2 && a == 3) {
            console.log('OK');
        }
        */
        // 方案一的第二种写法
        // var a = [1, 2, 3];
        // a.toString = a.shift;
        // if (a == 1 && a == 2 && a == 3) {
        //     console.log('OK');
        // }

        /*
            方案二:Object.defineProperty劫持对象中某个属性的操作
        */
        // 全局变量也是给window设置一个全局属性
        var i = 0;
        Object.defineProperty(window, 'a', {
            get() {
                // => 获取window.a的时候触发
                return ++i;
            },
            set() {
                // => 给window.a设置属性值得时候触发
            }
        });
        if (a == 1 && a == 2 && a == 3) {
            console.log('OK');
        }
    </script>
</body>
</html>

6、下面代码输出结果是什么?为什么
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
};
obj.push(1);
obj.push(2);
console.log(obj);

此题答案如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面试题6-关于面向对象的问题</title>
</head>

<body>
    <!-- 面试题6-下面代码输出结果是什么?为什么? -->
    <script>
        /*
            模拟Array.prototype.push方法
            Array.prototype.push = function push(){
                // => this :arr
                // this.length = this.length || 0;
                // => 拿原有的length作为新增项的索引
                this[this.length] = num;
                // => length的长度是会自动跟着累加1
            };
        */
        // let arr = [10, 20];//=> {0:10,1:20,length:2}
        // arr.push(30);

        let obj = {
            2: 3,
            3: 4,
            length: 2,
            push: Array.prototype.push
        };
        obj.push(1); //=>obj[2]=1 obj.length=3
        obj.push(2);//=>obj[3]=2 obj.length=4
        console.log(obj); //{2: 1, 3: 2, length: 4, push: ƒ}

        // 若对象中没有length,会自动转化为0
        let obj2 = {
            1: 10,
            push: Array.prototype.push
        };
        obj2.push(20); //=> obj[obj.length]=20; 如果length为undefined,那么会自动转化为索引为0:obj[0]=20;
        console.log(obj2);//0: 20, 1: 10, length: 1...
    </script>
</body>
</html>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值