函数声明和原型链

今天整理了一天的二叉树,却还是一头雾水,不知道大家有没有学习的好方法,之前以为前端不需要数据结构和算法如此精通。看了一些笔试题之后心灰意冷了。今后整理好会发布的奋斗相比起来函数声明和原型链问题更像脑筋急转弯一点。整理几个看起来有点绕的问题吧。

第一题:关于原型链和运算符优先级

        var A=function(name){
            if(name)this.name=name;//此时name=undefined所以不会符合条件
        }
        var B=function(name){
            this.name=name;//此时name=undefined
        }
        var C=function(name){
            this.name=name||"jon"
        };

        A.prototype.name="tom";
        B.prototype.name="tom";
        C.prototype.name="tom";

        //求下面三个输出值
        alert(new A().name);//tom
        alert(new B().name);//undefined
        alert(new C().name);//jon

这题考到的知识点是原型链,首先new A()是new一个带参数列表,也就生成了A的实例对象,但是我们没有传入参数(undefined),A的情况是先判断是否有name,因为在构造函数上name为undefined嘛,所以不会走到this.name=name这句话,于是它向原型对象上找这个属性,找到了Tom。

在“较真的前端”看到一道题,感觉有点类似,也拿出来和大家分享。

        function Foo(){
            getName = function(){//私有方法
                alert(1);
            };
            return this;
        }
        Foo.getName = function(){//静态方法
            alert(2);
        };
        Foo.prototype.getName = function(){
            alert(3);
        };
        var getName = function(){
            alert(4);
        };
        function getName(){
            alert(5);
        };

接来下是几个各种变形的问题:

Foo.getName();

此处是调用静态方法,无需实例化,所以答案是2

getName();

调用全局的getName()函数,本例有函数声明和函数表达式两种,函数声明被提升到作用域最前面,而函数表达式是创建函数时进行赋值,所以答案为4。且需要等到表达式赋值那句话才能调用(因为是函数,如果是变量则为空),之前调用的话是:

Uncaught TypeError: getName is not a function

 Foo().getName();

先执行Foo函数,将getName赋值function(){alert(1);};注意此刻getName是没有var的,所以先向当前Foo函数作用域内寻找getName变量,再向当前作用域的上层寻找(即Window),也就是第二问的alert(4)函数。于是改变了window的getName函数。之后它返回的是this,这个this指的是window,所以此句相当于window.getName()。考察了变量作用域和this指向问题

getName();

直接调用getName()函数相当于调用window.getName(),但已经在第三问的时候改了,于是答案是1

new Foo.getName();

运算符优先级!!! 点的优先级>new无参数;点运算完后有个括号(),此时变成了new有参数列表,所以直接执行new,new有参数>函数调用。答案是2

new Foo().getName();

相当于(new Foo()).getName();首先new有参数列表(18)跟点的优先级(18)是同级,同级的话按照从左向右的执行顺序,所以先执行new有参数列表(18)再执行点的优先级(18),最后再函数调用(17)。括号内是优先级。也就是先new了一个构造函数Foo的实例对象,new出来的实例对象的this指向的就是这个实例对象本身。因为构造函数本身没有赋值this.getName,所以只能去原型链上找,于是找到了3

new new Foo().getName();

最后一问是上面一题的变形,相当于(new (new Foo()).getName)();将上一问的getName(即原型链上的)当作构造函数再实例化一个对象,所以结果依然是3

第二题:变量表达式提前但赋值不提前

        var a="00";
        (function(){
            alert(a);//输出undefined
            var a="01";//声明虽然提前了,但是赋值没有提前
        })();

可以结合上一题的扩展题中的函数声明一起体会一下!幸好此处是变量,如果此处是函数的话就会报错啦!因为函数调用要在函数表达式赋值之后(除非你还有个函数声明,即匿名函数那种形式)。

第三题:function的实例是啥类型

(typeof (new (class { class () {} })))

拆开来看

var Test = class{
    class(){}
};
typeof Test;//"function"
var test = new Test();
typeof test;//"object"

这里的难点反而不是es6语法了。请谨记new关键字做了什么:

  1. 创建一个对象obj
  2. 将此对象obj的__proto__指向构造器的prototype
  3. this指向新对象obj,并用新创建的对象obj执行构造函数
  4. 返回这个对象obj

所以test作为Test的实例表示新返回的对象,typeof test自然为"object"。

第四题:不存在的变量也能用typeof

(function() {
    var a=b=3;
})();
console.log(typeof a!=='undefined');//false
console.log(typeof b!=='undefined');//true

个人觉得这道题的难点不在于从左到右声明,从右到左赋值。而是原本不存在于window中的a竟然可以用typeof来访问。。。

第五题:没有var不给提升

console.log(a);//undefined
console.log(b);//b is not defined
var a = b = 2;

第六题:new String(),String()和直接字符串字面量

如第四题所言,new会返回一个新的对象,所以只要看见new,object的一系列相关问题就都要带上。String()与直接字面量是没有区别的。

var obj1 = new String("wihihoeid");
var obj2 = new String("wihihoeid");
console.log(obj1 == obj2);//false
console.log(obj1 === obj2);//false

var obj3 = "wihihoeid";
var obj4 = String("wihihoeid");
console.log(obj3 == obj4);//true
console.log(obj3 === obj4);//true

猜你喜欢

转载自blog.csdn.net/weixin_40322503/article/details/79942350
今日推荐