原帖地址:http://stephenzhao.github.io/2016/08/19/Front-end-Job-Interview-Questions/

最近我也是经历过面试别人和去面试的人了,总结几个常被提及的面试问题,做一下解答和备忘。

JavaScript 中 this 是如何工作的 ?

先来看看这个题目:

var x = 0;
var foo = {
x:1,
bar:{
x:2,
baz: function () {
console.log(this.x)
}
}
} var a = foo.bar.baz
foo.bar.baz() //
a() //
 
  • this 永远指向函数运行时所在的对象,而不是函数创建时所在的对象
  • 匿名函数和不处于仍和对象中的函数,This指向window
  • call, apply, with指的This是谁就是谁。
  • 普通函数调用,函数被谁调用,This就指向谁

上面的例子中,baz被bar调用所以指向的指bar. a 运行时所在的对象是 window,所以指向的是window。

作用域链

理解执行环境和上下文

函数调用都有与之相关的作用域和上下文。从根本上说,作用域是基于函数(function-based)而上下文是基于对象(object-based)。换句话说,作用域是和每次函数调用时变量的访问有关,并且每次调用都是独立的。上下文总是关键字 this 的值,是调用当前可执行代码的对象的引用。

执行上下文分有global、function、eval,一个函数可以产生无数个执行上下文,一系列的执行上下文从逻辑上形成了 执行上下文栈,栈底总是全局上下文,栈顶是当前(活动的)执行上下文。

执行上下文三属性:this指针,变量对象(数据作用域),作用域链

作用域链 即:一变量在自己的作用域中没有,那么它会寻找父级的,直到最顶层。过程如下:

  • 任何在执行上下文时刻的作用域都有作用域链来实现
  • 在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性
  • 在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.
    下面给出一个函数
    上面的文字大家可以好好琢磨一下,可以更好的理解函数作用域。

函数声明提升和变量声明提升(Hoisting)

我们先来了解js编译器在执行代码的过程:
以一段function代码为例:
第一步:创建可执行上下文(以下简称为EC),压入当前的EC栈中。EC中包括了以下信息:

  • 词法环境(=环境记录项(保存变量、函数声明和形参)+ 外部词法环境(function的[[scope]]属性,作用域链的本质))
  • this的指针
  • 变量环境(与环境记录项的值相同,但不再发生变动。)
    第二步:收集函数声明、变量声明和形参,保存在环境记录项内。这个收集的过程,就是一般所谓的声明提升现象的本质。如果发现了重复的标识符,则优先级为函数声明 、形参 、var声明(优先级低的会被无视)。

第三步:开始执行代码,环境记录项内没有的标识符会根据作用域链查找标识符对应的值,环境记录项亦有可能因赋值语句而被修改。

第四步:函数执行完毕,EC栈被弹出、销毁。

好了,第二步说的很清楚了 声明提升(Hoisting)现象就是在收集函数、变量声明和形参的过程会根据函数声明、形参、变量声明的顺序优先级来收集。

例子:

var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a);
// 输出1 由于函数声明提升,b内的实际是这样:
// function b() {
// var a;
// a = 10;
// return;
// function a() {}
// }

理解了吗?

什么是闭包,如何使用它,为什么要使用它?

还是上面的题目,做个变形。

 
var x = 0;
var foo = {
x:1,
bar:function () {
console.log(this.x);
var that = this;
return function () {
console.log(this.x)
console.log(that.x)
}
}
} foo.bar() //
foo.bar()() // this: 0, that: 1

解释一下程序执行的套路:

当一个函数被调用的时候,它从第一个语句开始执行,并在遇到函数体的关闭括号}时结束。
那使得函数把控制权交给调用该函数的程序部分。
return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下
的语句。
一个函数总是会返回一个值。如果没有指定返回值,则返回undefined。
如果函数以在前面加上new前缀的方式来调用,则返回值不是一个对象,则返回this(该
新对象)。

好了,我们理解了上面的套路,下面来解释闭包就好理解了。

闭包就是能够读取其它函数内部变量的函数

在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”

var x = 0;
var bar:function () {
var n = 999;
return function () {
return n;
}
}
var outer = bar();
outer() //

用途:

  1. 读取函数内部的变量
  2. 让这些变量的值始终保持在内存中

我们修改一下上面的代码

var add;
var bar = function () {
var n = 999;
add = function () {
n += 1;
}
return function () {
return n;
}
}
var outer = bar();
outer() // 999
add();
outer(); //

说明,n一直保存在内存当中,而没有在bar()执行完成之后被销毁;
原因:
bar里面的匿名函数被赋值给了outer,这个导致在outer没有被销毁的时候,该匿名函数一直存在内存当中,而匿名函数的存在依赖于bar,所以bar需要使用都在内存当中,所以bar并不会在调用结束后呗垃圾回收机制给收回。

而上面的add接受的也是一个匿名函数,该匿名函数本身也是闭包,所以也可以在外部操作里面的变量。

注意点

  1. 会导致内存泄漏,慎用
  2. 闭包会修改内部变量的值,所以在使用闭包作为对象的公用方法时要谨慎。
    闭包的一个应用,单例模式

单例模式的定义是产生一个类的唯一实例

单例模式在js中经常会遇到,比如 var a = {}; 其实就是一个单例子。

但是我们写一个更有意义的单例:

var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}

更简洁一点的:

var singleton = (function () {
var instance;
return function (object) {
if(!instance){
instance = new object();
}
return instance;
}
})();

(转) 前端面试之js相关问题(一)的更多相关文章

  1. 2019前端面试系列——JS面试题

    判断 js 类型的方式 1. typeof 可以判断出'string','number','boolean','undefined','symbol' 但判断 typeof(null) 时值为 'ob ...

  2. 2019前端面试系列——JS高频手写代码题

    实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...

  3. 【面试问题】—— 2019.3月前端面试之JS原理&CSS基础&Vue框架

    前言:三月中旬面试了两家公司,一家小型公司只有面试,另一家稍大型公司笔试之后一面定夺.笔试部分属于基础类型,网上的复习资料都有. 面试时两位面试官都有考到一些实际工作中会用到,但我还没接触过的知识点. ...

  4. 前端面试(原生js篇) - DOM

    根据我的面试经历,一般小公司的面试环节,比较关心框架的熟练程度,以及独立开发组件的能力 但大厂通常有五轮以上的面试,而且对 js 基础语法很是看重 于是我总结了一些关于 js 基础的面试对话,有的当时 ...

  5. 【前端知识体系-JS相关】深入理解MVVM和VUE

    1. v-bind和v-model的区别? v-bind用来绑定数据和属性以及表达式,缩写为':' v-model使用在表单中,实现双向数据绑定的,在表单元素外使用不起作用 2. Vue 中三要素的是 ...

  6. 前端面试(原生js篇) - 精确运算

    一.面试题 问:开发的时候有用到过 Math 吗? 答:很多啊.比如生成 GUID 的时候,就会用到 Math.random() 来生成随机数. 问:别的呢?比如向下取整.向上取整? 答:向下取整是  ...

  7. 前端面试问题js汇总

    1.javascript的typeof返回哪些数据类型 Object number function boolean underfind 2,数组方法pop() push() unshift()shi ...

  8. 【前端知识体系-JS相关】JS-Web-API总结

    2.1 DOM操作 2.1.1 DOM的本质是什么? <!-- DOM树:二叉树 --> /* <?xml version="1.0" encoding=&quo ...

  9. 【前端知识体系-JS相关】JS基础知识总结

    1 变量类型和计算 1.1 值类型和引用类型的区别? 值类型:每个变量都会存储各自的值.不会相互影响 引用类型:不同变量的指针执行了同一个对象(数组,对象,函数) 1.2 typeof可以及检测的数据 ...

随机推荐

  1. mysql 聚集和非聚集索引 解析

    一.聚集索引(聚簇索引) 1. 什么是聚集索引? 比如要查找'hello',则直接找内容为hello的行,我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”.   聚集索引的叶子节点 ...

  2. Kali-linux Gerix Wifi Cracker破解无线网络

    Gerix Wifi Cracker是另一个aircrack图形用户界面的无线网络破解工具.本节将介绍使用该工具破解无线网络及创建假的接入点. 9.3.1 Gerix破解WEP加密的无线网络 在前面介 ...

  3. [Python 网络编程] TCP、简单socket模拟ssh (一)

    OSI七层模型(Open System Interconnection,开放式系统互联) 应用层 网络进程访问应用层: 为应用程序进程(例如:电子邮件.文件传输和终端仿真)提供网络服务: 提供用户身份 ...

  4. [转]DbHelper通用数据库访问帮助类

    之前我一直都是在博客园中查看各位高手的博文,确实学到了不少知识,不过比较少写博客,现在就把我自己在项目实施过程中使用到比较好的技术框架写出来,希望能让更多的人了解和学习. 通常我们在开发使用数据库访问 ...

  5. php7.0编译安装

    1.解压安装  tar zxvf php-7.0.2.tar.gz  cd php-7.0.2 2.安装依赖库: yum -y install libjpeg libjpeg-devel libpng ...

  6. Springboot中使用ibatis输出日志

    logging.level.org.apache.ibatis=DEBUG logging.level.org.mybatis=DEBUG logging.level.java.sql.Connect ...

  7. java alibaba fastJson 遍历数组json

    import java.util.*; import com.alibaba.fastjson.*; public class Test { public static void main(Strin ...

  8. IIS中ASP.NET虚拟目录不继承主站点web.config设置的办法(转载)

    ASP.NET提供了强大的Web.config来配置网站,一般来说一个网站只有一个根目录下的Web.config文件,有时候我们希望子目录有着不同的权限或者参数设置,则可以在相应子目录增加一个Web. ...

  9. HDU 3695 Computer Virus on Planet Pandora (AC自己主动机)

    题意:有n种病毒序列(字符串),一个模式串,问这个字符串包括几种病毒. 包括相反的病毒也算.字符串中[qx]表示有q个x字符.具体见案列. 0 < q <= 5,000,000尽然不会超, ...

  10. 设置eclipse默认编码格式

    Window->Preferences->General ->Content Type->Text->JSP 最下面设置为UTF-8 Window->Prefere ...