变量 & 作用域
什么是变量 ?
变量,是计算机内存中存储数据的标识符,根据变量名,可以获取到内存中存储的数据。
为什么要使用变量 ?
根据变量可以方便的获取或者修改内存中的数据。
变量
ECMAScript 的变量是松散类型的。所谓松散类型,就是可以用来保存任何类型的数据,即:
- 声明时不用给变量指定数据类型;
- 赋值时可以修改数据类型;
简单来讲,每个变量仅仅是一个用于保存值的占位符而已。
[ 声明与赋值 ] 定义变量时要使用 var (或者 let、const — 块级声明?!)操作符,后跟变量名即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var abc; // 声明时,如果没有赋值,会保存为一个特殊的值 undefined var xyz = 'xyz'; // 声明的同时又进行了赋值(直接初始化变量) xyz = 123; // 更改为其他类型(有效,但不建议修改变量所保持的值的类型,因为没必要) // 可以使用一条语句定义多个变量,只要把每个变量(初始化或不初始化均可)用逗号分开即可 var abc = 'abc',xyz = 'xyz',num = '123'; // 同时声明多个变量 function demo(){ // 使用 var 定义的变量将成为定义该变量的作用域中的局部变量; // 也就是说,如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁,具体见“作用域”。 // 省略 var 操作符可以定义全局变量,但不推荐这样的做法(难以维护)。 var abc = abcd = 'abc'; // 这种写法,会产生一个全局变量 abcd,应当避免 } demo(); console.log(abcd); // =>abcd |
[ 全局变量 ] 在全局 { 作用域?↓ } 中定义的变量以及没有使用 var、let 或者 const 定义的变量。
1 2 3 4 5 6 7 8 9 10 |
var abc = 'abc'; let xyz = 'xyz'; console.log(abc); // =>abc console.log(xyz); // =>xyz console.log(window.abc); // =>abc console.log(window.xyz); // undefined function func(){ abc = 'abc'; } func(); console.log(abc); // =>abc console.log(window.abc); // =>abc |
[ 直接量 ] 又称字面量,用来表示值,它们是固定的值,不是变量。在 JavaScript 中,直接量可以分为:
- 字符串直接量:"abc" "123"
- 模板字符串直接量:
abc
123
- 数字直接量:123 0.123
- 布尔直接量:true false
- 正则直接量:/ab+/g
- Null直接量:null
- 数组直接量:[] [1,2,3]
- 对象直接量:{} {name:'tom',age:25}
变量的解构赋值( ES6 )
在 ES6 中,按照一定模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构赋值。
从本质上来讲,这种模式属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
数组解构赋值与对象结构赋值的差异:
- 数组元素是按次序排列的,变量取值由位置决定;
- 而对象没有次序,变量名必须与属性名相同,才能取到正确的值。
[1] 数组的解构赋值
[ 基本用法 ]
1234 let [foo,[bar],baz]=[1,[2],3]foo // 1bar // 2baz // 3如果解构不成功,变量的值就为 undefined
1234 let [x,y,...z]=['a']x // 'a'y // undefinedz // [][ 默认值 ] 解构赋值允许指定默认值。
但是,ES6 内部使用严格相等于(===)来判断一个位置是否有值,如果数组的成员不严格等于 undefined,默认值就不会生效。即,一个位置的值不是 undefined,那么即使设置了默认值,也不会有效。
1234567 let [foo=true]=[];foo // truelet [x=1]=[undefined]x // 1let [x=1] =[null]x // null
[2] 对象的解构赋值
1234 let {foo,bar}={foo:'abc',bar:'xyz'}foo // 'abc'bar // 'xyz'# 当变量名和属性名相同时
123456 let {foo,bar}={foo:'abc',bar:'xyz'}foo // 'abc'bar // 'xyz'上述代码的实质应该是:let {foo:foo,bar:bar}={foo:'abc',bar:'xyz'}// 当变量名和属性名一样时,可以简写 {foo,bar}来代替{foo:foo,bar:bar}对象解构赋值的内部机制:是先找到同名属性,然后再赋值给对应的变量。真正赋值的是后者,而不是前者
# 当变量名与属性名不同时
1234 let {foo:hello,bar:world}={foo:'abc',bar:'xyz'}hello // 'abc'world // 'xyz'foo // error: foo is not definedfoo 是匹配的模式,hello 才是真正的变量
[3] 字符串的解构赋值
字符串结构赋值的时候,字符串被转换成了一个类似数组的对象
123456 const [a,b,c,d,e]='hello'a // 'h'b // 'e'c // 'l'd // 'l'e // 'o'// 这种类数组的对象,有 length 属性,因此,也可以对 length 属性进行解构赋值
12 let {length:len}='hello'len // 5
[4] 数值和布尔值的解构赋值
数值和布尔值进行解构赋值的时候,会先转换为对象
12345678 let {toString:s} = 123s // Number.prototype.toStringlet {toString:s} = trues // Boolean.prototype.toString//数值对象和布尔值的包装对象都有toString属性,因为变量s可以取到值解构赋值的规则:只要等号右边的值不是数组或者对象,就先将其转化为对象,由于 null 和 undefined 无法转化为对象,所以对它们解构赋值会报错
12345 let {proxy:x}=undefined;x // TypeErrorlet {proxy:y} = null;y // TypeError
[5] 函数参数的解构赋值
12345 function add ([x,y]){return x+y;}add([1,2]) // 3
[6] 用途
[ 交换变量的值 ] 没有解构赋值的情况下,交换两个变量需要一个临时变量
12345 let x=1;let y=2;[x,y] = [y,x]x // 2y // 1
[ 从函数中返回多个值 ] 从一个函数返回一个数组是十分常见的情况。但是,函数只能返回一个值,如果需要返回多个值,只能将他放在数组或者对象里返回,有了结构赋值,取出这些值就非常方便
123456789101112131415161718192021 // 返回数组function example(){return [1,2,3]}let [a,b,c]=example()a // 1b // 2c // 3// 返回对象function example(){return {foo:1,bar:2}}let [foo,bar]=example()foo // 1bar // 2[ 函数参数的定义 ] 解构赋值,可以很方便的将一组参数与变量名对应起来
[ 提取 JSON 数据 ] 解构赋值在提取 JSON 数据尤为有用
12345678910 let jsonData = {id:42,status:"OK",data:[23,45]}let {id,status,data}=jsonDataconsole.log(id,status,data)// 42,"OK",[23,45]
[ 定义函数参数的默认值 ] 避免在函数体内部再写 var foo = config.foo || "default foo"这样的语句
1234567891011 jQuery.ajax= function(url,{asyc=true,beforeSend=function(){},cache=true,complete=function(){},crossDomain=false,global = true,// .... more config}){// ... do stuff}
[ 遍历 Map 结构 ] 任何部署了 Iterator 接口的对象都可以使用 for...of 循环遍历。
Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值都非常方便
12345678910111213141516171819 var map = new Map()map.set("first",'hello')map.set("second",'world')for (const [key,value] of map) {console.log(key + "is" +value)}// first is hello// second is world// 只想获取键名for (const [key] of map) {//}// 只想获取键值for (const [,value] of map) {//}
[ 引入模块中的某些方法 ]
1 cosnt {sourceMapConsumer,SourceNode} = require('source-map')