用es5如何实现 let 和 const ?
简介
let
和const
语法是es6
中定义变量的操作符, 作用就像es5
中的var
一样, 但是又不一样, 具体的介绍以及区别本文就不探讨了, 有需要的小伙伴可以移步阮一峰老师的这本书中查看: ES6 入门教程, 书中介绍地十分详细, 而且还有其他es6
相关语法的介绍
核心思路
核心思路在于实现es6
的块级作用域
和值不可变
的特性, 这里的值不可变
的特性特指const
, 确切的说是同一个内存地址中的数据不可修改
块级作用域
而es5
中并没有块级作用域
这个概念, 只有函数作用域
的概念, 比如如下的一段代码:
function foo() {
var a = 1;
}
console.log(a);
这时浏览器就会报错:Uncaught ReferenceError: a is not defined
同时我们在使用webpack
来将模块化
的代码做打包操作的时候, 打包之后的代码也是使用函数作用域
来隔离作用域
, 从而使得我们开发的各个模块
代码之间互不影响, 确切的说是使用了IIFE
值不可变
而值不可变
的特性主要是要区分基本类型值
和我们的复合类型值
, const
保证的只是指针总是指向相同的地址, 而指向的数据结构是否可变就不可控了, 基本类型值
中, 变量指向的地址就是数据保存的地址, 而复合类型值
中则不同, 变量指向的地址保存的是指针, 指针指向实际数据的地址, const
保证的是指针的指向是固定的, 比如:
const a = 1;
a = 2;
此时浏览器就会报错: Uncaught TypeError: Assignment to constant variable.
但如果是复合类型值
, 比如对象
, 那么结果就会有所不同:
const obj = {a: 1, b: 2};
obj.c = 3;
console.log(obj); //{a: 1, b: 2, c: 3}
obj = {};
obj.c = 3;
这句是可以执行的, 执行之后obj
的值从{a: 1, b: 2}
变为了{a: 1, b: 2, c: 3}
, 可是当我们执行obj = {};
这一句的时候就报错了: Uncaught TypeError: Assignment to constant variable.
这是因为一开始, 变量obj
的指针指向的是{a: 1, b: 2}
, 而我们修改了指针的指向, 让它指向了一个新的值{}
, 因此就报错了, 违背了const
指针指向不可变的规则
了解了这些之后就可以着手用es5
实现es6
中的let
和const
了
实现let
使用IIFE
可以方便的创造出一个块级作用域
, 而在这个作用域
之外的地方访问其中的变量
都会报错, 既隔离了作用域
, 也防止了变量声明提升
的发生:
try{
console.log('变量声明提升a', a);
}catch(error) {
console.error('变量未定义');
}
(function(){
var a = 1;
console.log('内部a:', a);
})();
try{
console.log('外部a:', a);
}catch(error) {
console.error('变量未定义');
}
此时浏览器中的输出结果为:
变量未定义
内部a: 1
变量未定义
这就实现了我们的let
实现const
那么接下来就是实现我们的const
了, const
除了要创造块级作用域
之外还要处理它的值不可变
的特性, 这里的值还要区分基础类型值
和复合类型值
基础类型值
中我使用的方法是定义另一个变量
来保存之前的值, if
判断如果之前的值被修改了就抛出错误并且再改回来
复合类型值
的赋值操作是引用
的赋值, 就是变量
不同, 但指针指向的是同一个内存地址中的数据, 可以理解为不同变量
指向了同一个对象
, 以及==
或者===
是比较的指向是否相同, 相同则返回true
, 不同则返回false
, 比如:
//两个变量中指针指向的是两个不同的内存地址中的对象, 因为每次变量的声明赋值操作都会开辟新的内存空间,
//所以两次变量声明赋值, 开辟了两个内存空间, 它们的地址是不一样的, 哪怕两个对象里的数据结构是一样的
var obj = {a: 1, b: 2};
var obj2 = {a: 1, b: 2};
console.log(obj == obj2); //false
console.log(obj === obj2); //false
但此时就是相等的了:
var obj = {a: 1, b: 2};
var obj2 = obj; //将obj的对象引用赋值给obj2, 或者说将obj2指向obj
//obj和obj2都指向同一个对象
console.log(obj == obj2); //true
console.log(obj === obj2); //true
最终实现const
的代码如下:
try{
console.log('变量声明提升a', a);
}catch(error) {
console.error('变量未定义');
}
(function(){
var a = {a: 1, b: 2};
//对象
if(Object.prototype.toString.call(a) === '[object Object]') {
var b = a;
a.c = 3;
//或者
// a = {};
if(a !== b) {
console.error('变量不能再次赋值');
a = b;
}
console.log(a);
}else{
//基础类型值
var prev = a;
a = 2;
if(a !== prev) {
console.error('变量不能再次赋值');
a = prev
}
console.log(a);
}
})();
try{
console.log('外部a:', a);
}catch(error) {
console.error('变量未定义');
}