1.5JavaScript语言的变量(Variable)

1.变量声明

注意:在同一个作用域,相同名称的变量不可以重复声明,否则会报错。

//声明后再初始化
let 变量名[ = undefined];
变量名 = 值;
//声明时同时初始化
let 变量名 = 值;

变量在赋值后可以通过重新赋值改变其值。

注意:新的值的类型不一定与老的值的类型相同。

let a = 1;
a = { name: "张三" };
console.log(a);  // { name: "张三" }

2.变量声明提升(Hoisting)

变量声明不支持提升。

console.log(a);  // 报错
let a = 1;

3.变量作用域

变量的作用域是块作用域,即外围离变量最近的代码块。全局变量的作用域是定义它们的文件。

3.1全局变量

名称修饰符
全局变量let

3.2成员变量

注意:静态字段、实例字段、数据属性声明时,变量名前务必不要添加 let 关键字。

名称修饰符
静态字段(类)static
实例字段(类)——————
数据属性(对象字面量)——————

3.3局部变量

名称修饰符
局部变量let
值形参——————
默认值形参——————
剩余形参...

4.赋值

当我们将一个原始类型的值赋给变量时,我们是将原始类型的值本身存储到了变量中。

当我们将一个引用类型的值(即对象)赋给变量时,我们并没有将对象本身存储到了变量中,而是将对象的引用存储到了变量中。

5.变量引用表达式

变量名;

6.复制值

通过变量将一个原始类型的值赋给另一个变量时,原始类型的值会被复制到新变量中。此时,两个变量是独立的,互不干扰。

let a = 1;
let b = a;
console.log(b);  // 1
a = 2;
console.log(b);  // 1

通过变量将一个引用类型的值(即对象)赋给另一个变量时,对象的引用会被复制到新变量中。此时,因为两个变量都存储的是同一个对象的引用,所以一个对象上的变化会在另一个对象上反应出来。

let o1 = { a: 1 };
let o2 = o1;
console.log(o2.a);  // 1
o1.a = 2;
console.log(o2.a);  // 2

7.解构(Destructuring)赋值

解构赋值用于在一条语句中给多个(或一个)变量(或常量)赋值。

数组字面量解构赋值是按位置赋值,而对象字面量解构赋值是按命名赋值。

注意:赋值表达式右侧的数组字面量和对象字面量是复合数据结构,所以只占用一个值的空间,同理,赋值表达式左侧的类似于数组字面量和对象字面量的复合数据结构也只占用一个变量或常量的空间。

注意:对赋值表达式左侧变量的值的更改不会反应到赋值表达式右侧原来的数组字面量或对象字面量。

注意:与普通赋值不同,解构赋值是并行赋值,最常见的示例就是使用解构赋值交换两个变量的值。

//使用普通赋值交换两个变量的值
let a = 1;
let b = 2;
let arr = [b, a];
a = arr[0];
b = arr[1];
console.log(a, b);  // 2 1

//使用数组字面量解构赋值交换两个变量的值
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);  // 2 1

//使用对象字面量解构赋值交换两个变量的值
let a = 1;
let b = 2;
({ a, b } = { a: b, b: a });
console.log(a, b);  // 2 1

7.1数组字面量解构赋值

7.1.1用于变量赋值

let [变量名, 变量名 = 默认值, ...剩余变量名] = 可迭代对象;
//左侧数量等于右侧数量
let [a, b] = [1, 2];
console.log(a, b);  // 1 2
//左侧数量小于右侧数量
//右侧多余的元素会被忽略
let [a, b] = [1, 2, 3];
console.log(a, b);  // 1 2

//右侧多余的元素会被打包成数组字面量
let [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(rest);  // [3, 4, 5]
//左侧数量大于右侧数量
//左侧多余的变量会被赋予undefined值
let [a, b, c] = [1, 2];
console.log(a, b, c);  // 1 2 undefined

//左侧多余的变量会被赋予默认值
let [a, b, c = 3] = [1, 2];
console.log(a, b, c);  // 1 2 3
//左侧数组为稀疏数组(一个空槽也占一个元素的位置)
//空槽在前面
let [ , a, b] = [1, 2, 3];
console.log(a, b);  // 2 3

//空槽在中间
let [a, , b] = [1, 2, 3];
console.log(a, b);  // 1 3

//注意:末尾的逗号是为了添加一个新元素时方便,不是空槽。
let [a, b, ] = [1, 2, 3];
console.log(a, b);  // 1 2
//给已声明的变量赋值时,不需要在赋值表达式左侧添加let关键字。
let a, b;
[a, b] = [1, 2];
console.log(a, b);  // 1 2

实际上,赋值表达式左侧内的变量名可以是任何可赋值的标识符,比如对象字面量的属性访问表达式。

let o = {};
[o.a, o.b] = [1, 2];
console.log(o.a, o.b);  // 1 2

实际上,赋值表达式右侧可以是任何可迭代(Iterable)对象。

let [a, b] = "12";
console.log(a, b);  // 1 2
let [a, b] = new Set([1, 2]);
console.log(a, b);  // 1 2

赋值表达式右侧内的元素还可以是函数声明。

//命名函数
let [a, b] = [1, function f() {
  return 2;
}];

//匿名函数
let [a, b] = [1, function () {
  return 2;
}];

//箭头函数
let [a, b] = [1, () => {
  return 2;
}];

//都输出一样
console.log(a, b());  // 1 2

7.1.2用于函数形参

规则参考上面的“用于变量赋值”章节。

function f([a, b]) {
  console.log(a, b);
}

f([1, 2]);  // 1 2  

7.2对象字面量解构赋值

7.2.1用于变量赋值

let { 属性名: 变量名, 属性名, 属性名 = 默认值, 属性名: 变量名 = 默认值, ...剩余变量名 } = 对象字面量;
//左侧数量等于右侧数量
//属性名与变量名不相同(前面a是右侧对象字面量内的属性名,后面x是变量名)
let { a: x, b: y } = { a: 1, b: 2 };
console.log(x, y);  // 1 2

//属性名与变量名相同(前面a是右侧对象字面量内的属性名,后面a是变量名)
let { a: a, b: b } = { a: 1, b: 2 };
//简写
let { a, b } = { a: 1, b: 2 };
console.log(a, b);  // 1 2
//左侧数量小于右侧数量
//右侧多余的属性会被忽略
let { a, b } = { a: 1, b: 2, c: 3 };
console.log(a, b);  // 1 2

//右侧多余的属性会被打包成对象字面量
let { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4, e: 5 };
console.log(rest);  // { c: 3, d: 4, e: 5 }
//左侧数量大于右侧数量
//左侧多余的变量会被赋予undefined值
let { a, b, c } = { a: 1, b: 2 };
console.log(a, b, c);  // 1 2 undefined

//左侧多余的变量会被赋予默认值
let { a, b, c = 3 } = { a: 1, b: 2 };
console.log(a, b, c);  // 1 2 3
//给已声明的变量赋值时,不需要在赋值表达式左侧添加let关键字,但赋值表达式必须被包裹在一对圆括号内。
let a, b;
({ a, b } = { a: 1, b: 2 });
console.log(a, b);  // 1 2

实际上,赋值表达式左侧内的变量名可以是任何可赋值的标识符,比如数组的元素访问表达式。

let arr = [];
({ a: arr[0], b: arr[1] } = { a: 1, b: 2 });
console.log(arr[0], arr[1]);  // 1 2

注意:如果赋值表达式右侧是原始类型的值,则原始类型的值会被自动转换成对象,但是 undefinednull 不会被自动转换成对象。

let { constructor: c } = 8;
console.log(c === Number);  // true

let { length } = "foobar";
console.log(length);  // 6


let { _ } = undefined;
console.log(_);  // 报错
let { _ } = Object(undefined);
console.log(_);  // undefined

let { _ } = null;
console.log(_);  // 报错
let { _ } = Object(null);
console.log(_);  // undefined

赋值表达式右侧内的属性值还可以是函数声明。

//命名函数
let { a, b } = { a: 1, b: function f() {
  return 2;
}};

//匿名函数
let { a, b } = { a: 1, b: function () {
  return 2;
}};

//箭头函数
let { a, b } = { a: 1, b: () => {
  return 2;
}};

//都输出一样
console.log(a, b());  // 1 2

7.2.2用于函数形参

规则参考上面的“用于变量赋值”章节。

function f1( { a: x, b: y } ) {
  console.log(x, y);
}

function f2( { a, b } ) {
  console.log(a, b);
}

f1({ a: 1, b: 2 });  // 1 2
f2({ a: 1, b: 2 });  // 1 2

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/javascript/javascriptlang/4734.html

(0)
huoxiaoqiang的头像huoxiaoqiang
上一篇 2020年8月4日 02:23
下一篇 2020年8月6日 20:07

相关推荐

  • 2.1JavaScript语言的ES模块(Module)

    ES 模块用于在浏览器环境和服务器环境中使用。 模块就是以 .js 为扩展名的 JavaScript 文件。 普通脚本文件内的顶级的成员对其它脚本文件来说是公开(public)的全局上下文,而模块文件内的顶级的模块成员对其它模块文件来说都是私有(private)的,所以首先需要在模块中将它们显式导出,然后在其它模块中显…

  • 2.9JavaScript引用类型之生成器(Generator)

    Generator 类型实现了 Iterable 接口。 1.生成器函数声明 生成器函数声明只需要在函数名前面加一个星号 * 即可,星号 * 不受两侧空格的影响。 生成器函数支持函数声明、函数表达式,不支持箭头函数表达式。 2.yield和yield * yield 关键字用来多次暂停和恢复一个生成器函数,它…

  • 1.3JavaScript语言的非运算符(Non-Operator)和运算符(Operator)

    1.非运算符 非运算符 描述 空白 空格(U+0020)、水平制表符(\t,U+0009)、垂直制表符(\v,U+000B)、分页符(\f,U+000C)。 行结束符 换行符(\n,U+000A)、回车符(\r,U+000D)、行分隔符(U+2028)、段落分隔符(U+2029)。 , 逗号 ; 分号 () 圆括号 […

发表回复

登录后才能评论