1.1 原始值和引用值
ECMAScript变量可以包含两种不同类型数据:原始值和引用值。
- 原始值:最简单的数据(基本数据类型)
- 引用值:由多个值构成的对象(引用类型)
1.2 值复制
复制原始值时:修改复制后的值,原先的值不回被改变
复制引用值时:修改复制后的值,原先的值会被改变,因为它复制的是地址(指向对象的指针)而不是值
1.3 执行上下文
每一段程序都有很多外部变量。一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行。你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。
1.4 变量作用域
变量在声明它的函数体和嵌套的(父级,不包括同级和子级)函数体内都是有定义的。
换句话说,变量声明在全局作用域中均有效。
例:
var scope = 'global';
function t() {
var scope;
console.log(scope);//undefined
scope = 'local';
console.log(scope);//local
}
t();
因此函数内声明的 scope 覆盖了全局变量 scope
又因为没有给它赋值,所有输出的是 undefined
var name = 'global';
if (true) {
var name = 'local';
console.log(name);
};
console.log(name);
同理由于变量在本身函数体内和嵌套的函数体内都有定义,因此后面定义的 name 会覆盖前一个 name 并作用于嵌套函数体中。
1.5 作用域链
因为变量定义在父子级中都可以使用,如果需要重复定义变量值,则会有访问时到底采用哪个变量值的问题,因此定义作用域链来规定变量的访问顺序。
(注:函数参数被认为是当前上下文的变量)
先看一段代码:
name = 'xxh';
function t() {
var name = 'txxh';
function s() {
var name = 'sxxh';
console.log(name);//sxxh
}
function ss() {
console.log(name);//txxh
}
s();
ss();
}
t();
函数调用时会创建一个包含其中变量的对象。
当调用函数 s 时,将创建函数的执行环境(对象),并将该对象置于链表开头,然后将外部函数 t 的对象链接在之后,最后是全局对象。
在函数执行时,从作用域链中查找变量,以便读、写值。
本作用域链为 s ( ) => t ( ) => window。
1.6 作用域链增强
-
with语句
with语句主要用来增强作用域链,将语句的对象添加到作用域链的头部。
person = {
name: 'xxh',
age: 24,
height: 175,
wife: {
name: 'undefined',
age: 24
}
};
with (person.wife) {
console.log(name);
}
作用域链为:person.wife => window
with语句结束后,作用域链恢复正常
function bulidUrl() {
let qs = '?debug=true';
with (location) {
let url = href + qs;
}
return url;
}
作用域链:location => bulidUrl => window
1.7 变量声明
ES6之前,var 都是声明变量的唯一关键字。ES6不仅增加了 let 和 const 两个关键字,而且还让这两个关键字压倒性的超越 var 成为首选。
-
var 声明
var 声明会被拿到函数或全局作用域的顶部,位于作用域中的所有代码之前。这个现象叫 “提升” 。
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
let result = add(10, 20);
console.log(sum);//报错,这里sum不是有效变量
- let 声明
let 和 var 很相似,但它的作用域是块级的,这是JavaScript的新概念。块级作用域由最近的一对包含花括号 { } 界定。
function foo() {
let c;
}
console.log(c); // undefined ,因为 let 声明只在块级作用域中才能访问
{
let d;
}
console.log(d); //undefined
且不能对同一个变量重复进行 let 声明:
function foo() {
let c;
}
console.log(c); // undefined ,因为 let 声明只在块级作用域中才能访问
{
let d;
}
console.log(d); //undefined
let 声明变量只在块级作用域中有效的特点,非常适合在循环中声明迭代变量。
for (var i = 0; i < 10; ++i) { }
console.log(i);
for (let j = 0; j < 10; ++j) { }
console.log(j);
使用var声明的迭代变量会泄露到循环外部;
let则不回,let非常适合在循环中声明迭代变量
-
const 常量声明
使用 const 声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任何时候都不能重新复制。但如果声明的是引用类型,可添加属性。
const a;//报错,没有初始化
const b = 3;
console.log(b);
b = 4;//报错,一经初始化不能重新赋值