面试-JS总结

160 阅读5分钟

1.let、const、var区别

let: 声明变量,不存在变量提升(必须先声明,再使用)。具有暂时性死区(在代码块内,使用let命令声明变量之前,该变量都是不可用的),声明的变量仅在块级作用域内有效(在let命令所在的代码块内有效)。不允许重复声明(不允许在相同作用域内,重复声明同一个变量。)

const: 声明一个只读的常量,声明时必须赋值(不赋值会报错)。和let一样具有不存在变量提升、暂时性死区、不允许重复声明、只在声明所在的块级作用域内有效的特性。

var: 声明变量,存在变量提升的现象。没有暂时性死区,允许重复声明,ES6之前只有全局作用域和函数作用域,没有块级作用域。

2.箭头函数与普通函数的区别

箭头函数:this对象是定义时所在的对象。不可以当作构造函数。没有arguments对象。不可以使用yield命令,因此箭头函数不能用作 Generator函数。没有prototype原型对象。

普通函数:this对象是使用时所在的对象。可以当做构造函数。可以使用arguments对象。可以使用yield命令,用作 Generator 函数。含有prototype原型对象。

3.ES5与ES6继承的不同

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

4.js模块化(commonjs/AMD/CMD/ES6)

Commonjs,NodeJS是该规范的主要实践者,有四个重要的环境变量:==module==、==exports==、==require==、global。module.exports定义当前模块对外输出,用require加载模块。CommonJS模块输出的是一个值的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值。

// 定义模块math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { //在这里写上需要向外暴露的函数、变量
  add: add,
  basicNum: basicNum
}

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);

// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);

CommonJS是==同步加载模块==。在服务端,模块文件存于本地磁盘中,读取速度快,同步加载不会有问题。但是在浏览器中,由于网络原因,应该使用异步加载模块的方式,就是接下要提到的==AMD==规范。

AMD规范采用异步加载模块的方式,需要依赖加载的模块语句都放在回调函数中,等待依赖的模块加载完成再执行。require.js实现了AMD规范:用require.config()指定项目中用到的基础模块的引用路径,用define()定义模块,用require()加载模块

/** 网页中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模块 **/
// 首先用config()指定各模块路径和引用名
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //实际路径为js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 执行基本操作
require(["jquery","underscore"],function($,_){
  // some code here
});

// 定义math.js模块
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
// 定义一个依赖underscore.js的模块
define(['underscore'],function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young';
    })
  };
  return {
    classify :classify
  };
})

// 引用模块,将模块放在[]内
require(['jquery', 'math'],function($, math){
  var sum = math.add(10,20);
  $("#sum").html(sum);
});

CMD是另一种js模块化的规范,它与AMD很相似。不同点在于AMD先加载所有依赖模块,再执行回调函数,不论回调函数中是否应用到该依赖模块。CMD则推崇依赖就近,延迟执行。sea.js在推广过程中实现了CMD。

/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面声明并初始化了要用到的所有模块
    a.doSomething();
    if (false) {
        // 即便没用到某个模块 b,但 b 还是提前执行了
        b.doSomething()
    } 
});

/** CMD写法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要时声明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});

/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
    var sum = math.add(1+2);
});

ES6 Module:ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

5.promise、async await、Generator的区别

/*
假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
*/

//promise方案:Promise会含有大量的then,catch的API,操作本身的语义反不清晰。
function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值
  let ret = null;

  // 新建一个空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有动画
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // 返回一个部署了错误捕捉机制的Promise
  return p.catch(function(e) {
    /* 忽略错误,继续执行 */
  }).then(function() {
    return ret;
  });

}


//generator方案:Generator虽然语义上比Promise方案清晰,但是需要用户声明自动执行函数,而且必须保证yield语句后面的表达式,必须返回一个Promise。
function chainAnimationsGenerator(elem, animations) {

  return spawn(function*() {
    let ret = null;
    try {
      for(let anim of animations) {
        ret = yield anim(elem);
      }
    } catch(e) {
      /* 忽略错误,继续执行 */
    }
    return ret;
  });

}

//async方案:async函数实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。
async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}

6.js隐式转换

隐式转换

7.babel编译原理(未完成)