在ES5中,变量声明只有var和function以及隐式声明三种,在ES6中则增长了let,const,import和class四种。javascript
ES5中最原始的变量声明,用于声明变量,其实JavaScript是弱类型语言,对数据类型变量要求不太严格,因此没必要声明每个变量的类型(这就是下面说的隐式声明,固然这并非一个好习惯),在使用变量以前先进行声明是一种好的习惯。 java
1.做用域 node
使用var声明的变量的做用域是函数做用域(在ES5时代,只有函数做用域和全局做用域两种做用域),在一个函数内用var声明的变量,则只在这个函数内有效。jquery
function test(){
var a;
console.log(a);//undefined
}
console.log(a);//ReferenceError: a is not defined
2.变量声明提高es6
用var声明变量时,只要在一个函数做用域内,不管在什么地方声明变量,都会把变量的声明提高到函数做用域的最前头,因此不管使用变量在变量声明前仍是声明后,都不会报错(固然只是声明提早,赋值并无提早,因此若是使用在声明以前,会输出undefined,但不会报错)。 浏览器
function test(){
console.log(a);//undefined
var a=3;
}
当没有声明,直接给变量赋值时,会隐式地给变量声明,此时这个变量做为全局变量存在。 babel
function test(){ a=3; console.log(a);//3 } test();
console.log(a);//3
固然要注意,隐式声明的话就没有变量声明提早的功能了,因此下面的使用是会报错的。 markdown
function test(){
console.log(a);//ReferenceError: a is not defined
a=3;
}
用function声明的是函数对象,做用域与var同样,是函数做用域。 函数
function test(){
function a(){
console.log('d');
}
a();//'d'
}
a();//ReferenceError: a is not defined
一样,function声明也有变量声明提高,下面是两个特殊的例子: 测试
function hello1(a){
console.log(a); //[Function: a]
function a(){}
console.log(a);//[Function: a]
}
hello1('test');
function hello2(a){
console.log(a); //test
var a=3;
console.log(a);//3
}
hello2('test');
这里有涉及到函数中形参的声明,咱们能够将以上两个例子当作:
function hello1(a){ var a='test; console.log(a); //[Function: a] function a(){} console.log(a);//[Function: a]
}
hello1('test');
function hello2(a){ var a='test; console.log(a); //test var a=3; console.log(a);//3 } hello2('test');
能够看到函数对象的声明也提早了,可是在形参变量声明以后(形参的变量声明在全部声明以前)。
当函数对象和普通对象同时声明时,函数对象的声明提早在普通对象以后。
function test(){
console.log(a);//[Function: a]
function a(){}
var a;
console.log(a);//[Function: a]
}
ES6新增的声明变量的关键字,与var相似。
固然,与var也有很大区别:
1.做用域不一样
let声明的变量的做用域是块级做用域(以前的js并无块级做用域,只有函数做用域和全局做用域),var声明的变量的做用域是函数做用域。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
2.不存在变量声明提高
用var声明变量时,只要在一个函数做用域内,不管在什么地方声明变量,都会把变量的声明提高到函数做用域的最前头,因此不管使用变量在变量声明前仍是声明后,都不会报错。而let不同,与java以及其余语言同样,let声明的变量,在未声明以前变量是不存在的。(js的语法愈来愈向java靠拢)
console.log(a); // undefined,可是不报错。
console.log(b); // ReferenceError: b is not defined.
var a = 2;
let b = 2;
注意:在使用babel时可能会遇到这样的状况:
console.log(b); //undefined
let b = 2;
babel在翻译es6时,彷佛直接将let变为了var,因此运行时也有变量声明提高了,可是在Chrome下运行时是正确的。
3.暂时性死区
所谓暂时性死区,意思是,在一个块级做用域中,变量惟一存在,一旦在块级做用域中用let声明了一个变量,那么这个变量就惟一属于这个块级做用域,不受外部变量的影响,以下面所示。
不管在块中的任何地方声明了一个变量,那么在这个块级做用域中,任何使用这个名字的变量都是指这个变量,不管外部是否有其余同名的全局变量。
暂时性死区的本质就是,只要一进入当前做用域,所要使用的变量就已经存在了,可是不可获取,只有等到声明变量的那一行代码出现,才能够获取和使用该变量。
暂时性死区的意义也是让咱们标准化代码,将全部变量的声明放在做用域的最开始。
var a = 123;
{
console.log(a);//ReferenceError
let a;
}
4.不容许重复声明
在相同的做用域内,用let声明变量时,只容许声明一遍。 (var是能够屡次声明的)
// 正确
function () {
var a = 10;
var a = 1;
}
// 报错,Duplicate declaration "a"
function () {
let a = 10;
var a = 1;
}
// 报错,Duplicate declaration "a"
function () {
let a = 10;
let a = 1;
}
const用来声明常量,const声明的常量是不容许改变的,只读属性,这意味常量声明时必须同时赋值, 只声明不赋值,就会报错,一般常量以大写字母命名。
阮一峰大神的书里说,在严格模式下,从新给常量赋值会报错,普通模式下不报错,可是赋值无效。可是测试了一下,不管是严格仍是非严格模式,都会报错。
const A = 1;
A = 3;// TypeError: "A" is read-only
const和let相似,也是支持块级做用域,不支持变量提高,有暂时性死区.
注意:若是声明的常量是一个对象,那么对于对象自己是不容许从新赋值的,可是对于对象的属性是能够赋值的。
const foo = {};
foo.prop = 123;
foo.prop// 123
foo = {} // TypeError: "foo" is read-only
ES6采用import来代替node等的require来导入模块。
import {$} from './jquery.js'
$对象就是jquery中export暴露的对象。
import命令接受一个对象(用大括号表示),里面指定要从其余模块导入的变量名。注意:大括号里面的变量名,必须与被导入模块对外接口的名称相同。
若是想为输入的变量从新取一个名字,import命令要使用as关键字,将输入的变量重命名。
import { New as $ } from './jquery.js';
注意,import命令具备提高效果,会提高到整个模块的头部,首先执行。
ES6引入了类的概念,有了class这个关键字,固然,类只是基于原型的面向对象模式的语法糖,为了方便理解和开发而已,类的实质仍是函数对象,类中的方法和对象其实都是挂在对应的函数对象的prototype属性下。
咱们定义一个类:
//定义类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
setSex(_sex) {
this.sex=_sex;
}
}
constructor方法,就是构造方法,也就是ES5时代函数对象的主体,而this关键字则表明实例对象,将上述类改写成ES5格式就是:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype. setSex = function (_sex) {
this.sex=_sex;
}
因此说,类不算什么新玩意,大多数类的特性均可以经过以前的函数对象与原型来推导。
1.全部类都有constructor函数,若是没有显式定义,一个空的constructor方法会被默认添加(有点相似java了)。固然全部函数对象都必须有个主体。
2.生成类的实例对象的写法,与ES5经过构造函数生成对象彻底同样,也是使用new命令。
class B {}
let b = new B();
3.在类的实例上面调用方法,其实就是调用原型上的方法,由于类上的方法其实都是添加在原型上。
b.constructor === B.prototype.constructor // true
4.与函数对象同样,Class也可使用表达式的形式定义。
let Person = class Me {
getClassName() {
return Me.name;
}
};
至关于
var Person = function test(){}
5.Class其实就是一个function,可是有一点不一样,Class不存在变量提高,也就是说Class声明定义必须在使用以前。
全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。
ES5之中,全局对象的属性与全局变量是等价的,隐式声明或者在全局环境下声明的变量是挂在全局对象上的。
ES6规定,var命令,function命令以及隐式声明的全局变量,依旧是全局对象的属性;而let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。
var a = 1;
console.log(window.a) // 1
let b = 1;
console.log(window.b) // undefined
函数的形参,隐藏着在函数一开始声明了这些形参对应的变量。
function a(x,y){}
能够当作
function a(){
var x=arguments.length <= 0 || arguments[0] === undefined ? undefined : arguments[0];
var y=arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1];
}
固然在ES6下默认声明就是用的let了,因此函数a变成:
function a(){
let x=arguments.length <= 0 || arguments[0] === undefined ? undefined : arguments[0];
let y=arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1];
}
因此在ES6中会有如下几个问题:
function a(x = y, y = 2) {
return [x, y];
}
a(); // 报错,给X赋值时y还未被let声明。
function a(x,y) {
let x;//至关于重复声明,报错。
}