前言
众所周知,let
、const
与var
的最主要区别是变量提升、块级作用域和声明变量不可变
,因此想要模拟let
和const
要先抛出三个核心问题。
1、怎样用
var
实现块级作用域效果?2、怎样用
var
使声明的变量不可修改?3、怎样用
var
使变量不可重复声明?
前置知识-先了解javascript的三种作用域
先回顾更好地理解javascript
作用域才能更好地往下思考实现效果。
全局作用域
所谓全局,简单说就是所有地方都能访问到的变量。不包含在任何作用域内声明的变量都是全局作用域,即不在任何函数或者大括号里面。
直接上示例
scss
var a = 1;
function test() {
var b = 2;
function testbar(c) {
console.log(a + b + c); // 输出6
}
testbar(3);
}
test();
变量a
和方法test
都在最外层都是全局作用域中,内层可以访问外层的变量,最终结果输出6
函数作用域
函数作用域顾名思义函数
,因此在JavaScript
规则中,所有定义在某个函数体内的变量都无法被函数外访问,因为这些变量的作用域仅限于这个函数内部。
直接上示例和效果
ini
function test() {
var age = 123;
}
test();
console.log(age);

结果会无法访问,并且报错ReferenceError: *** is not defined
块级作用域
ES6
引入let
和const
关键字之后才出现块级作用域,因此let
和const
才会出现块作用域var
不会。
简单说就是在{}
内定义的变量和常量就算是一个块级作用域,在这个{}
之外是无法访问到的。
直接看示例对比区别
ini
function test() {
if(true){
var age = 123;
let age2 = 456;
}
console.log("age", age);
console.log("age2", age2);
}
test();

效果如上图所示,如果使用var
不会有块级作用域能直接获取到值,但使用let
和const
会有块级作用域。
一、模拟let块级作用域
javascript
如果提到作用域除了let
你还想到了什么??
那必须是想到函数作用域
啊,可以使用立即执行函数(IIFE
),在 javascript
中的作用域规则决定了它会立即执行,然后创建一个新的作用域,并且内部变量与外面的变量相互独立从而达到效果。
常规使用let
或const
示例效果
javascript
{
let x = 88;
console.log('里面x', x); // 正常输出 88
}
console.log('外面x', x);
如图所示,在块级作用域会报错is not defined
使用var
模仿效果
javascript
{
(function() {
var x = 88;
console.log('里面x', x); // 输出 88
})();
console.log('外面x', x); // 报错
}
在上述例子中,我们用立即执行函数包住var x = 88
, 这样就使作用域限制在函数内部,从而实现了类似let
或const
的块级作用域效果。
二、模拟const声明变量后不可变
实现这个小功能其实有几种方法我们一个个说。但声明后不可以重新赋值,这句话你第一时间能想到什么JavaScript
的API
???没错就是Object.defineProperty
, 如果你对这个API
不熟悉的话,建议你去学习一下,下面有我之前写的相关文章链接。
方法一:Object.defineProperty
理解vue2响应式原理核心Object.defineProperty
直接上个例子
javascript
var testConst = {};
Object.defineProperty(testConst, 'val', {
value: 10,
writable: false,
configurable: false,
});
console.log('修改前', testConst.val); // 打印:10
testConst.val = 20; // 由于writable为false所以不生效
console.log('修改后', testConst.val); // 打印:10
通过设置writable: false
,使属性不可变,修改也不会生效。
注意: 严格模式下会抛出TypeError
,原因是writable
设置为false
方法二:Proxy代理拦截
如果你对这个API
不熟悉的话,建议你去学习一下,下面有我之前写的相关文章链接。
直接上代码例子
javascript
var testConst = new Proxy({}, {
value: 10,
set(target, prop, value) {
if (prop === 'value') {
return false; // 修改属性时,直接返回false
}
target[prop] = value;
return true;
}
});
console.log('修改前', testConst.value); // 10
testConst.value = 20; // 不生效
console.log('修改后', testConst.value); // 10
主要核心在set
方法强行拦截处理,禁止对指定属性进行一切修改从而实现功能。
方法三:用闭包实现
直接上示例代码
javascript
function createConst(value) {
return {
get value() {
return value;
}
};
}
var testConst = createConst(10);
console.log('修改前', testConst.value); // 10
testConst.value = 20; // 不生效,因为并没有set方法
console.log('修改后', testConst.value); // 10
核心思路是只设置了一个get
方法,不设置set
方法,这样在修改指定属性的时候,就会不会生效。
其实还有其它方法,就先说这三个吧....
三、使用var实现不可重复声明的变量
众所周知,使用var
声明的变量是可以重复声明的,并且后面会覆盖前面的变量,而同一作用域内let
和const
不可以重复声明。
那么如何用var
实现相似效果呢???一听到不重复这个词,小脑瓜一转第一反应想到的是Map()
。我们可以通过calss
封装一个类进行统一调用,达到模拟的效果。
基本封装如下代码所示
kotlin
class ImitateVar {
constructor() {
this.scopevar = new Map();
}
// 用于声明变量
declareVar(name, value) {
if (this.scopevar.has(name)) {
return false;
}
this.scopevar.set(name, value);
return true;
}
// 获取变量
get(name) {
if (this.scopevar.has(name)) {
return this.scopevar.get(name);
}
return undefined;
}
// 设置变量
set(name, value) {
if (!this.scopevar.has(name)) {
return false;
}
this.scopevar.set(name, value);
return true;
}
}
这里简单讲解分析一下这个ImitateVar
类。
其中constructor
初始化一个 Map
用来存储指定变量;
declareVar
用来声明变量,并且添加判断,如果变量已经存在,则抛出异常,如果变量不存在,则添加到Map
中;
get
获取变量的值,并且判断变量是否存在,如果存在,则返回变量的值,否则输出错误信息;
set
设置变量的值,并且判断变量是否存在,如果存在,则设置变量的值,否则输出错误信息;
下面来看看基本使用
csharp
var imitateVar = new ImitateVar();
// 声明
imitateVar.declareVar('myVar', 10);
console.log('看看myVar', imitateVar.get('myVar')); // 10
// 尝试重复声明
if (!imitateVar.declareVar('myVar', 20)) {
console.log('重复声明失败');
}
console.log('再次看看myVar', imitateVar.get('myVar')); // 10
// 修改变量
imitateVar.set('myVar', 25);
console.log('修改后的myVar', imitateVar.get('myVar')); // 25
上述示例分别进行了创建、重复创建、获取和修改操作,其中imitateVar.declareVar('myVar', 20)
如果重复会返回false
。
小结
这个问题其实考的是基本功是否扎实,能不能快速想到能实现小需求的相应知识点。
var
和lat
与const
的不同之处其实还有其它知识点,但这里主要说了一下比较核心的几个知识点,毕竟如果面试官真的问到你这个问题你说出这几个点已经是基本功就好的了。
好听这文章先写到这里,如果那里写的不对或者有更好的建议欢迎指出啊。