3道题考察你对JavaScript函数、变量、对象、作用域的理解程度

 

小试牛刀

本篇文章从一道题开始,后面会逐步的在这道题的基础上进行拓展,先思考一下执行完下面的代码后输出的是什么

getAge()
var getAge = function(){
    console.log(18);
}
function getAge(){
    console.log(20);
}
getAge()

这道题考到了两个知识点,一点是在JavaScript中的变量提升,另一点是函数的定义有函数声明与函数表达式两种类型。

什么是变量提升

  • 函数及变量的声明都将被提升到作用域的最顶部。

  • 变量可以在使用后声明,也就是变量可以先使用再声明。

函数声明

fun()
function fun(){
    console.log('函数声明');
}
fun()

上面这是一个函数声明的方式,按照变量提升机制,这里的fun()会被提升到最顶部,因此这里的结果就显而易见了。

/*答案*/
fun()    //函数声明
function fun(){
    console.log('函数声明');
}
fun()    //函数声明

通过这个例子我们可以得到,函数声明是在JS解析时进行函数提升,因此在同一个作用域内,无论函数在哪里定义声明的,该函数都可以调用。

函数表达式

fun()
var fun = function(){
    console.log('函数表达式');
}

表达式声明是将函数赋值给一个变量,这个变量当然也会被提升到最前面,但是这里存在一个坑。变量提升只是把变量提升到了最前面,但是赋值是在JS运行时进行的。我们来看一下输出结果,显示fun不是一个函数。

fun()  //Uncaught TypeError: fun is not a function
var fun = function(){
    console.log('函数表达式');
}

把fun()放到后面看一下呢

var fun = function(){
    console.log('表达式声明');
}
fun()   //表达式声明

已经可以正常执行fun 函数,因为此时已经被赋值了。

了解完上面的内容后,回头看看开始的问题,答案如下

getAge()   // 20
var getAge = function(){
    console.log(18);
}
function getAge(){
    console.log(20);
}
getAge()   //18

函数对象

在JavaScript中函数也可以是对象,它拥有自己的构造函数,也有原型链。同样以一道思考题开始。

function Person(){
    getAge = function(){
        console.log(17);
    }
    this.getAge = function(){
        console.log(18);
    }
}
Person.getAge = function(){
    console.log(19);
}
Person.prototype.getAge = function(){
    console.log(20);
}
Person.getAge()
new Person().getAge()

这道题主要考察对象的私有属性/方法、公有属性/方法、静态属性/方法。

  • 公有属性/方法必须实例化才可以使用,也就是new操作。

  • 公有方法不可以调用私有方法可静态方法。

  • 私有属性和私有方法不可以在外部被访问到。

  • 静态属性和静态方法无需实例化就可以访问。

Person.getAge() 这里没有对Person进行实例化,因此是调用的Person的静态方法。

new Person().getAge() 这一步先对Person进行了实例化,然后调用了实例的getAge()方法。其实本质是这样的顺序(new Person())getAge()。但是现在有个问题,Person 在构造函数与原型链中都有getAge()方法。这种情况下会默认使用构造函数中的公有方法,而不是原型中的。

function Person(){
    function getAge(){   //私有方法
        console.log(17);
    }
    this.getAge = function(){   //公有方法
        console.log(18);
    }
}
Person.getAge = function(){   //静态方法
    console.log(19);
}
Person.prototype.getAge = function(){    //公有方法
    console.log(20);
}
Person.getAge()    //19
new Person().getAge()    //18

this的指向

这道题相对之前的就要难一些了,这里主要是对this和作用域的考察。

function Person(){
    this.getAge = function(){
        console.log(18);
    }
    getAge = function(){
        console.log(19);
    }
    return this
}
var getAge = function(){
    console.log(20);
}
​
getAge()
Person().getAge()
new Person().getAge()
getAge()

第一问:getAge() ,这个比较简单,没有什么可以讲解的,就是单纯的执行getAge函数,答案是:20

第二问:Person().getAge()先执行了Person()函数,然后调用Person函数返回的对象的getAge属性函数。其中的getAge = function(){console.log(19);}是一赋值语句,在当前作用域中没有声明getAge,因此它会向上层作用域寻找变量getAge。在外面var getAge = function(){console.log(20);}变量被提升,因此这里相当于修改了全局变量getAge 。函数返回的this在这里是指的window,因为这里是执行Person()函数,而并没有实例化。所以Person().getAge()相当于调用window下的getAge 方法,最终输出结果为:19。

第三问:与第二问不同的是,这里对Person进行了实例化,所以这里返回的this指返回的实例,然后调用该实例的公有方法,也就是this.getAge,答案就是:18。

第四问:这问看起来与第一问差不多,通过第二问的分析,此时的全局变量getAge在第二问时已经被修改了,所以这里的输出为19。

构造函数的返回值

在传统语言中,构造函数是没有返回值的,实际的返回值就是此构造函数的是实例化的对象。

在JavaScript中的构造函数可以有返回值,也可以没有,这里可以分为3种情况。

  • 没有返回值时,返回的是实例化对象。

function Person(name){
    this.name = name
}
console.log(new Person('bbb').name);   //bbb
  • 有返回值,但是返回的内容为值类型而非引用类型,如String,Number,Boolean,Null,Undefined),这种情况与没有返回值的情况相同,返回的仍然是返回的是实例化对象。

function Person(name){
    this.name = name
    name = 'aaa'
    return name
}
console.log(new Person('bbb').name);   //bbb
  • 有返回值,返回的值为引用类型。那么实际返回的就是该返回值。

function Person(name){
    this.name = name
    name = 'aaa'
    return name
}
console.log(new Person('bbb').name);   //bbb

 

你可能感兴趣的:(JavaScript)