开始
先说一下JS中有哪六种变量的声明方法吧,然后我们带着三个问号去看文章:what?how? where?
六种声明变量的方法:
- var
- let
- const
- function
- import
- class
没有对比就没有伤害(var、let、const)
面试中,有不少的面试官喜欢问let、var、const的区别,虽然这个问题很简单,但是从这道题中,可以看出你的编码习惯和风格;
有的同学表示很疑惑了,从这道题怎么会看出自己的编码习惯和风格呢,那么好,疑问三连送给你:
- 你见过全是var变量的JavaScript吗?
- 你经历过滥用let的后果吗?
- 你知道const的命名规范吗?
下面我们来看一下它们之间的区别 (如果大佬需要补充,请到评论区):
var | let | const |
---|---|---|
存在变量提升 | 不存在变量提升 | 不存在变量提升 |
代码块外部可调用 | 存在块级作用域 | 存在块级作用域 |
赋值后值不可改变 |
通过上面的表格我们可以看到它们之间的大体区别,虽然差别不是很多,但是在实际开发中,却有着大大小小的坑
来看第一段代码(耐心看完下面三段代码,并不难哦):
function fun(str){
let str = 'hello'+'world!';
console.log(str);
}
fun('123');
没错,这是一段有问题的代码,运行后是一个语法错误:Uncaught SyntaxError:Identifier 'code' has already been declared,进一步解析一下它运行后的样子:
function fun(str){
var str; // 这个变量代表的是函数形参
let str = 'hello'+'world!';
// 这时候fun函数的作用域顶部,有两个变量名为str,所以报错!
console.log(str);
}
fun('123')
这也说明了在同一作用域中,任何变量是不可以重复声明的,我就是我,颜色不一样的烟火!
总结:这段代码需要注意的坑:作用域、变量提升、变量名禁止重复;
如果对上面的代码理解了,咱们来看第二段代码:
var str = 'hello';
function fun(){
console.log(str);
let str = 'world';
console.log(str);
}
fun();
只要块级作用域内存在let
命令,它所声明的变量就“绑定”这个区域,不再受外部的影响,这也就是传说中的 暂时性死区,ES6 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错,所以上面是一段错误代码:Uncaught ReferenceError: Cannot access 'str' before initialization
总结:这段代码需要注意的坑:暂时性死区、块级作用域
消化完上面代码,继续来看最后一段代码:
const obj = {};
let str = '坚持一周写两篇博客';
let addObj = obj.names = str;
console.log(addObj);
看到这段代码后,有的同学就会说了:你不是说const的值不能改变吗?你个骗子!const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
const需要注意的点:
- 只声明不赋值,就会报错
- 只在声明所在的块级作用域内有效
-
const
命令声明的常量也是不提升,同样存在暂时性死区 - 不可重复声明
- 想将对象冻结,应该使用
Object.freeze
方法
function、import、class
function
ES6规定:
- 允许在块级作用域内声明函数。
- 函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 - 同时,函数声明还会提升到所在的块级作用域的头部。
注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let
处理。
根据这三条规则,浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var
声明的变量。上面的例子实际运行的代码如下:
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
import
import
用于加载文件,在大括号接收的是一个或多个变量名,这些变量名需要与你想要导入的变量名相同。
举个栗子:你想要导入action.js文件中的某一个变量,这个变量里保存了一段代码块,所以你要写成:import { Button } from 'action'
,这个时候,你就从action.js中获取到了一个叫 Button
的变量,既然获取到了,你就可以对Button里的代码猥琐欲为了
如果想为输入的变量重新取一个名字,import
命令要使用as
关键字,将输入的变量重命名,比如:
import { NewButton as Button } from 'action.js';
上面的代码表示从action.js文件中,获取一个变量名称叫做Button的代码段,然后你又声明了一个变量 NewButton
,将 Button
保存在 NewButton
class
关于class,后期我会单独写一篇文章,详细的介绍一下,这里就简单说一下:
首先通过看ES5中的构造函数,然后再用ES6的class去实现:
// ES5写法 :
function fun ( x, y ){
this.x = x;
this.y = y;
};
fun.prototype.GetHair = function(){
return `${this.x}掉了两根头发,${this.y}说俺也一样!`;
};
let setHair = new fun('小明','老王');
console.log(setHair.GetHair()); // 小明掉了两根头发,老王说俺也一样!
再来看一下ES6的class写法:
class interest {
constructor( x, y, e, z ){
this.x = x;
this.y = y;
this.e = e;
this.z = z;
}
MyInterest(){
let arr = [];
console.log(`我会${[...arr,this.x,this.y,this.e,this.z]}!`);
}
}
let GetInterest = new interest('唱','跳','rap','篮球');
console.log(GetInterest.MyInterest()); //我会唱,跳,rap,篮球!
ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,constructor
方法就是构造方法,而this
关键字则代表实例对象。也就是ES5 的构造函数Point
,对应 ES6 的Point
类的构造方法。
结束
关于JS声明变量的六种方式就介绍完了,如果对你有帮助的话,就点个赞鼓励一下吧;
文章内容如有不同意见,欢迎评论区指出(初写文章,请多指教)~
本人微信:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。