从面试题谈谈js的闭包,原型

最近群里有小伙伴分享了两道面试题,这里我谈谈自己的理解,废话不多说,上第一题:

var n = 10;
var obj = {
    n:20,
    fn:(function(){
        this.n += 2;
        n *= 3;
        return function(){
            this.n *= 2;
            n += 1;
            console.log(n)
        }
    })(n)
}

var fn = obj.fn;
fn();
obj.fn()
console.log(n,obj.n)  

这个题目中,定义的obj对象的fn属性是个自执行的函数,任何自执行的函数,其内部this指向全局变量window,所以定义对象obj时,全局变量n由于自执行函数的影响,加上自执行函数内部没有定义n,所以会执行window.n*=2,接着执行window.n+=3,此时全局变量n已经被修改为36;

接下来

var fn = obj.fn;
fn(); 

这两步其实就是在全局空间定义一个变量fn,赋值为obj的fn属性,也就是那个自执行函数,然后再执行它本身,此时由于自执行函数返回的函数(闭包)中没有定义n,会向上一级作用域查找,就是全局变量n,之前说过,已经被修改成36,所以在执行时

this.n *= 2;  //36*2=72
n += 1;    //72+1=73
console.log(n)

fn()执行的结果就是打印73;

下面obj.fn(),fn()作为obj的方法执行,其this值指向obj本身,此时自执行函数返回的函数(闭包)中,this.n就修改了obj.n,使其值变为40,因为返回的函数没有定义n,会向上一级作用域查找,就是全局变量n,所以n+1=73+1=74,所以打印结果为74;

再接着打印全局变量n,值为74,obj.n上一步说了,是40。

ok,继续说下一道题目:

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();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();   

首先看这个题目开始做了些什么事情:首先定义了一个Foo函数,之后为Foo创建了一个名为getName的静态属性,并存储了一个匿名函数,之后为Foo的原型对象创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。

1.Foo.getName()

Foo.getName 访问Foo函数上存储的静态属性,即为2。

2.getName()

首先要知道的是,所有声明变量或声明函数都会被提升到当前函数的顶部,而函数表达式不会提前,所以题目的代码实际是下面这个样子

function Foo() {
 getName = function () { alert (1); };
 return this;
}
var getName;//提升变量声明
function getName() { alert (5);}//提升函数声明,覆盖var的声明

Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
getName = function () { alert (4);};//赋值再次覆盖function getName声明

getName();//最终结果输出4

3.Foo().getName()

Foo().getName(); 先执行Foo函数,然后调用Foo函数的返回值this的getName属性函数。Foo函数的第一句 getName = function () { alert (1); }; 是一句函数赋值语句,它没有var声明,所以先向当前Foo函数作用域内寻找getName变量声明,没有找到。继续向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二问中的alert(4)函数,将此变量的值赋值为 function(){alert(1)}。Foo函数返回的this是window对象,相当于执行 window.getName() ,而window中的getName已经被修改为alert(1),所以最终会输出1

4.getName()

直接调用getName函数,相当于执行 window.getName() ,因为这个变量已经被第三问Foo函数执行时修改了,所以结果与第三问相同,为1

5.

new Foo.getName();
new Foo().getName();
new new Foo().getName();   

关于以上三个含有new操作符的式子,一起说一下,这里主要是js中运算符优先级的问题,其实执行顺序依次如下

new (Foo.getName)();  

//点号优先级高于new,将getName函数作为了构造函数来执行,遂弹出2

(new Foo()).getName(); 

//括号优先级高于new,先执行Foo函数,此时返回的是this,而this在构造函数中本来就代表当前实例化对象,所以最终Foo函数返回实例化对象。之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,遂到当前对象的原型对象(prototype)中寻找getName,找到了,是3

new ((new Foo()).getName)();

//先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new。
最终结果为3

以上就是两道关于原型,作用域闭包,变量提升,以及运算符优先级的问题

时间: 2024-08-24 15:58:08

从面试题谈谈js的闭包,原型的相关文章

谈谈JS中的原型

不知道大家对JS中的原型理解的怎么样,我想如果大家对JS中的原型对象以及prototype属性十分熟悉的话对后面原型链以及继承的理解会十分的容易,这里想和大家分享自己对其的理解,请先看下面这段代码O(∩_∩)O~~ 1 function Person(){ 2 } 3 Person.prototype.name = "jingzi"; 4 Person.prototype.age = 20; 5 Person.prototype.sayName = function(){ 6 aler

前端面试题(JS篇)

原题地址:http://handyxuefeng.blog.163.com/blog/static/454521722013111714040259/ 好吧,最近打算换工作,所以关注比较多的是面试题,这套还不错,留下~~ 1.JS相关问题: 解释下事件代理(基础题) 解释下 JavaScript 中 this 是如何工作的(始终指向调用当前函数的对象) 解释下原型继承的原理. AMD vs. CommonJS? 什么是哈希表? 解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式):f

前端开发面试题收集 JS

前端开发面试题收集-JS篇 收集经典的前端开发面试题 setTimeout的时间定义为0有什么用? javascript引擎是单线程处理任务的,它把任务放在队列中,不会同步执行,必须在完成一个任务后才开始另一个任务. 由于setTimeout可以把任务从某个队列中跳出成为新队列,因此能够得到期望的结果. 怎么理解this this指向的总是调用函数的那个对象. this一般情况下,是全局对象Global. 什么是闭包 闭包是一个概念,我的理解是函数里的函数,能够读取函数内部变量的函数. 就是将函

谈谈JS中的面向对象

请先看看下面这段代 1 <script src="jquery.js"></script> 2 <script type="text/javascript"> 3 /** 4 * Object.create() 最近才添加进了ECMAScript第5版规范,有些浏览器不支持 5 * 这里模拟一个Object.create方法解决兼容性问题 6 * Object.create : 该方法只有一个参数,即原型对象,返回一个新对象 7

谈谈JS里的{ }大括号和[ ]中括号的用法

谈谈JS里的{ }大括号和[ ]中括号的用法,理解后就可以看懂JSON结构了. 一.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数. 如:var LangShen = {"Name":"Langshen","AGE":"28"}; 上面声明了一个名为"LangShen"的对象,多个属性或函数用,(逗号)隔开,因为是对象的属性, 所以访问时,应该用.(点)来层层访问:LangShe

JS之闭包

闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性: 1)作用域链 2)垃圾(内存)回收机制 3)函数嵌套...等等. 首先理解一下作用域链的含义,简单来说,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引. 而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止. 当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有

js中的原型、继承的一些想法

最近看到一个别人写的js类库,突然对js中的原型及继承产生了一些想法,之前也看过其中的一些内容,但是总不是很清晰,这几天利用空闲时间,对这块理解了一下,感觉还是有不通之处,思路上没那么条理,仅作为分享, 一.instanceof 在JavaScript有instanceof运算符,是二元运算符,使用方法 instanceA instanceof A,返回值是布尔值(boolean),含义是判断instanceA是否是A的一个实例,其实质是判断A.prototype===instanceA.__p

关于js中闭包的理解

1.以前很不理解js中闭包的概念及使用,下面来看一下 function foo() { var a = 123; var b = 456; return function () { return a; } } var fn = foo(); 上面的代码只能访问 a和b,但是不能修改,这是js中闭包的技术之一 function foo() { var a = 123; var b = 456; return { get_a: function () { return a; }, set_a: fu

JS基础——闭包

有关JS中闭包的理解和使用. 一.简介 子函数可以使用父函数中的局部变量,这种行为就叫做闭包.通常指,有权访问另一个函数作用域中的变量的函数.创建时,通常在一个函数中创建另一个函数,通过另一个函数访问这个函数的局部变量. function box() { var user = 'Lee'; return function () { //通过匿名函数返回 box()局部变量 return user; }; } alert(box()()); 这里通过一个匿名函数来访问父函数中的user变量,并且返