前端基础拾遗90问
本文转载于掘金的一位id为“写代码像蔡徐坤”的作者。链接为:https://juejin.im/post/5e8b261ae51d4546c0382ab4#heading-0
本文将从以下十一个维度为读者总结前端基础知识
JS基础
- 如何在ES5环境下实现let
这个问题实质上是在回答let和var有什么区别,对于这个问题,我们可以直接查看babel转换前后的结果,看一下在循环中通过let定义的变量是如何解决变量提升的问题。

babel在let定义的变量前加了道下划线,避免在块级作用域外访问到该变量,除了对变量名的转换,我们也可以通过自执行函数来模拟块级作用域。
(function(){ for(var i = 0; i < 5; i ++){ console.log(i) // 0 1 2 3 4 } })(); console.log(i) // Uncaught ReferenceError: i is not defined
不过这个问题并没有结束,我们回到var和let/const的区别上:
- var声明的变量会挂到window上,而let和const不会
- var声明的变量存在变量提升,而let和const不会
- let和const声明形成块作用域,只能在块作用域里访问,不能跨块访问,也不能跨函数访问
- 同一作用域下let和const不能声明同名变量,而var可以
- 暂时性死区,let和const声明的变量不能在声明前被使用
babel的转化,其实只实现了第2、3、5点。
- 2. 如何在ES5环境下实现const
实现const的关键在于Object.defineProperty()这个API,这个API用于在一个对象上增加或修改属性。通过配置属性描述符,可以精确地控制属性行为。Object.defineProperty() 接收三个参数:

对于const不可修改的特性,我们通过设置writable属性来实现
function _const(key, value) { const desc = { value, writable: false } Object.defineProperty(window, key, desc) } _const('obj', {a: 1}) //定义obj obj.b = 2 //可以正常给obj的属性赋值 obj = {} //无法赋值新对象
- 手写call()
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
语法:function.call(thisArg, arg1, arg2, ...)
call()的原理比较简单,由于函数的this指向它的直接调用者,我们变更调用者即完成this指向的变更:
//变更函数调用者示例 function foo() { console.log(this.name) } // 测试 const obj = { name: '写代码像蔡徐抻' } obj.foo = foo // 变更foo的调用者 obj.foo() // '写代码像蔡徐抻'
基于以上原理, 我们两句代码就能实现call()
Function.prototype.myCall = function(thisArg, ...args) { thisArg.fn = this // this指向调用call的对象,即我们要改变this指向的函数 return thisArg.fn(...args) // 执行函数并return其执行结果 }
但是我们有一些细节需要处理:
Function.prototype.myCall = function(thisArg, ...args) { const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性 thisArg = thisArg || window // 若没有传入this, 默认绑定window对象 thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数 const result = thisArg[fn](...args) // 执行当前函数 delete thisArg[fn] // 删除我们声明的fn属性 return result // 返回函数执行结果 } //测试 foo.myCall(obj) // 输出'写代码像蔡徐抻'

浙公网安备 33010602011771号