21. 作用域链?
原我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功
劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用
域链访问到父级里声明的变量或者函数。
由两部分组成:
- [[scope]] 属性: 指向父级变量对象和作用域链,也就是包含了父级的 [[scope]] 和 AO 。
- AO : 自身活动对象。如此 [[scopr]] 包含 [[scope]] ,便自上而下形成一条 链式作用域。
22. 对象的拷贝?
- 浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响。
- Object.assign
- 展开运算符( ... )
- 深拷贝 深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响 完全拷贝一个新对象,修改时原对象不再受到任何影响。
- JSON.parse(JSON.stringify(obj)) : 性能最快
- 具有循环引用的对象时,报错
- 当值为函数、 undefined 、或 symbol 时,无法拷贝
- 递归进行逐一赋值
23. new 运算符的执行过程?
- 新生成一个对象
- 链接到原型: obj.proto = Con.prototype
- 绑定 this: apply
- 返回新对象(如果构造函数有自己 retrun 时,则返回该值)
24. instanceof 原理?
- 新能在实例的 原型对象链 中找到该构造函数的 prototype 属性所指向的 原型对象,就返回 true 。即:
// __proto__: 代表原型对象链
instance.[__proto__...] === instance.constructor.prototype
// return true
25. 类型判断?
原我判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug ,本质原因是JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待。
- 基本类型( null ): 使用 String(null) 。
- 基本类型( string/number/boolean/undefined ) + function : - 直接使用 typeof 即可。
- 其余引用类型( Array/Date/RegExp Error ): 调用 toString 后根据 [object XXX] 进行判断。
26. 模块化?
原模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。
- 分类:
- es6: import / export
- commonjs: require / module.exports / exports
- amd: require / defined
- require 与 import 的区别:
- require 支持 动态导入, import 不支持,正在提案 ( babel 下可支持)
- require 是 同步 导入, impor t 属于 异步 导入
- require 是 值拷贝,导出值变化不会影响导入值; import 指向 内存地址,导入值会随导出值而变化。
27. 防抖与节流?
防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。
- 防抖 ( debounce ): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可:
function debounce(fn, wait, immediate) {
let timer = null
return function () {
let args = arguments
let context = this
if (immediate && !timer) {
fn.apply(context, args)
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
- 节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms 执行一次即可:
function throttle(fn, wait, immediate) {
let timer = null
return function () {
let args = arguments
let context = this
if (callNow) {
fn.apply(context, args)
callNow = false
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
}
28. 函数执行改变 this?
- 由于 JS 的设计原理: 在函数中,可以引用运行环境中的变量。因此就需要一个机制来让我们可以在函数体内部获取当前的运行环境,这便是 this 。
- obj.fn() ,便是 obj 调用了函数,既函数中的 this === obj
- fn() ,这里可以看成 window.fn() ,因此 this === window
- 三种方式可以手动修改 this 的指向:
- call: fn.call(target, 1, 2)
- apply: fn.apply(target, [1, 2])
- bind: fn.bind(target)(1,2)
29. ES6/ES7?
- 声明:
- let / const : 块级作用域、不存在变量提升、暂时性死区、不允许重复声明
- const : 声明常量,无法修改
- 解构赋值:
- calss/extend: 类声明与继承
- Set/Map: 新的数据结构
- 异步解决方案:
- Promise 的使用与实现
- generator: 1. yield : 暂停代码 2.next() : 继续执行代码
- await/async : 是 generator 的语法糖, babel 中是基于 promise 实现。
29. 说说你对 promise 的了解?
- 依照 Promise/A+ 的定义, Promise 有四种状态:
- pending: 初始状态, 非 fulfilled 或 rejected.
- ulfilled: 成功的操作.
- rejected: 失败的操作.
- settled: Promise 已被 fulfilled 或 rejected ,且不是 pending
- 另外, fulfilled 与 rejected 一起合称 settled
- Promise 对象用来进行延迟( deferred ) 和异步( asynchronous ) 计算
- Promise 的构造函数 的构造函数
- 构造一个 Promise ,最基本的用法如下:
var promise = new Promise(function (resolve, reject) {
if (...) { // succeed
resolve(result);
} else { // fails
reject(Error(errMessage))
}
})
- Promise 实例拥有 then 方法(具有 then 方法的对象,通常被称为 thenable )。它的使用方法如下:
promise.then(onFulfilled, onRejected)
- 接收两个函数作为参数,一个在 fulfilled 的时候被调用,一个在 rejected 的时候被调用,接收参数就是 future , onFulfilled 对应 resolve , onRejected 对应 reject。
30. 谈谈你对 AMD 、CMD 的理解?
- CommonJS 是服务器端模块的规范, Node.js 采用了这个规范。 CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。 AMD 规范则是非同步加载模块,允许指定回调函数。
- AMD 推荐的风格通过返回一个对象做为模块对象, CommonJS 的风格通过对 module.exports或 exports 的属性赋值来达到暴露模块对象的目的。
- es6 模块 CommonJS 、AMD 、CMD
- CommonJS 的规范中,每个 JavaScript 文件就是一个独立的模块上下文 modulecontext ),在这个上下文中默认创建的属性都是私有的。也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。
- CommonJS 是同步加载模块,在浏览器中会出现堵塞情况,所以不适用 AMD 异步,需要定义回调 define 方式 es6 一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用 export 关键字输出该变量es6 还可以出类、方法,自动适用严格模式。