不可变的原始值和可变的对象引用
当JavaScript中指定的值是原始值(基本类型)即 Boolean,null,undefined,String 和 Number 时,将分配实际值。原始值是不可更改的,
任何方法都无法更改或突变一个原始值。
let str1 = 'My string';
let str2 = str1;
str2 = 'My new string';
console.log(str1);
// 'My string'
console.log(str2);
// 'My new string'
但是,当指定的值是Array,Function 或 Object 时,将分配内存中对象的引用。对象和原始值不同,首先它们是可变的,它们的值是可修改的:
let obj1 = { name: 'Jim' }
let obj2 = obj1;
obj2.name = 'John';
console.log(obj1); // { name: 'John' }
console.log(obj2); // { name: 'John' }
原始值的比较是值的比较,只有在它们的值相等时它们才相等。而对象的比较均是引用的比较,对象值都是引用,当且仅当它们引用同一个基对象时,它们才相等。
//原始值的比较
let str1 = "hello";
let str2 = "hello";
console.log(str1 === str2); // true 值的比较
//引用不同的两个对象
let o = {x: 1}, p = {x: 1} //具有相同属性的两个对象
console.log(o === p); //false 两个单独的对象永不相等
console.log([] === []); //;两个单独的数组永不相等
//引用相同的两个对象
let a = [];
let b = a;
b[0] = 1;
console.log(a[0]); //1 变量a也会修改
console.log(a === b); // true a 和 b 引用同一个数组
闭包(Closure)
闭包是一种用于访问私有变量的重要JavaScript模式,可以访问当前作用域链上的变量。先看一个简单的例子:
function createGreeter(greeting) {
return function(name) { //返回的匿名函数可以访问 greeting
console.log(greeting + ', ' + name);
}
}
const sayHello = createGreeter('Hello');
sayHello('Joe');
// Hello, Joe
提到闭包,就不得不提到变量作用域这个概念,函数的执行依赖于变量作用域,要注意的是,这个作用域是在函数定义时决定的,而不是函数调用时决定的。看下边这个例子:
var scope = "global scope"; //全局变量
function fn() {
var scope = "local scope"; //局部变量
function f() { return scope; } //在作用域中返回这个值
return f();
}
fn(); // 输出 local scope
上个例子中,沿着作用域链向上查找,你应该很轻松得知输出的结果是 local scope。现在将这段代码稍作改动,你知道会返回什么吗?
var scope = "global scope"; //全局变量
function fn() {
var scope = "local scope"; //局部变量
function f() { return scope; } //在作用域中返回这个值
return f;
}
fn()(); // 输出 ?
在这段代码中,我们将函数内的一对圆括号移动到了 fn() 之后,fn() 现在仅仅返回函数内嵌套的一个函数对象,而不是返回结果,在定义函数作用域外面调用这个闭包会发生什么呢?上文提到,作用域链上函数定义时创建的,这个闭包定义在这个作用域里,所以不管在何时执行这个函数 f(), 返回的结果一定上定义的局部变量 local scope。
解构赋值(Destructuring)
在解构赋值中,右侧的数组或对象中的一个或多个值会被提取出来(解构),并赋值给左侧相应的变量名,这是一种从对象中干净地提取属性的常用方法。
const obj = {
name: 'lili',
age: 23
}
const { name, age } = obj;
console.log(name, age); // 'lili' 23
如果要以指定的名称提取属性,可以使用以下格式指定它们。
const obj = {
name: 'Lili',
age: 23
}
const { name: myName, age: myAge } = obj;
console.log(myName, myAge); // 'Lili' 23
解构赋值还可以直接用于提取函数参数(React 经常会使用)。
const person = {
name: 'Lili',
age: 23
}
function introduce({ name, age }) {
console.log(`我叫 ${name}, 我今年 ${age} !`);
}
console.log(introduce(person));
// "我叫 Lili 我今年 24!"
扩展运算符(Spread Syntax)
扩展运算符可以在函数调用/数组构造时, 将数组表达式在语法层面展开,还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。
等价于apply的方式:
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);
const path = require('path'
有了展开语法,可以这样写:
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);
所有参数都可以通过展开语法来传值,也不限制多次使用展开语法。
function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
数组拷贝(copy):
var arr = [1, 2, 3];
var arr2 = [...arr]; //类似 arr.slice()
arr2.push(4);
// arr2 此时变成 [1, 2, 3, 4]
// arr 不受影响
不使用展开语法,连接多个数组:
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// 将 arr2 中所有元素附加到 arr1 后面并返回
var arr3 = arr1.concat(arr2);
使用展开语法:
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];
Array.unshift 方法常用于在数组的开头插入新元素/数组. 不使用展开语法, 示例如下:
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// 将 arr2 中的元素插入到 arr1 的开头
Array.prototype.unshift.apply(arr1, arr2)
// arr1 现在是 [3, 4, 5, 0, 1, 2]
如果使用展开语法, 代码如下: (请注意, 这里使用展开语法创建了一个新的 arr1 数组, Array.unshift 方法则是修改了原本存在的 arr1 数组):
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1];
// arr1 现在为 [3, 4, 5, 0, 1, 2]
Rest 运算符(Rest Syntax)
用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function myFunc(...args) {
console.log(args[0] + args[1]);
}
myFunc(1, 2, 3, 4); // 3
生成器函数(Generators)
ES6 中的生成器函数,在yield关键字的帮助下可以返回多个值的函数。
注意在 function 关键字旁边使用*字符来表示它是一个生成器函数。现在让我们创建一个生成器的实例,并通过调用生成器上的next()并获取值:
function* greeter() {
yield 'Hi';
yield 'How are you?';
yield 'Bye';
}
const greet = greeter();
console.log(greet.next().value);
// 'Hi'
console.log(greet.next().value);
// 'How are you?'
console.log(greet.next().value);
// 'Bye'
console.log(greet.next().value);
// undefined
next() 返回一个带有值的对象 调用 done 属性返回布尔值,如果生成器超出值,则返回 true:
function* someGenerator(){
yield 'Cats';
yield 'Dogs';
yield 'Birds';
}
const gen = someGenerator();
console.log(gen2.next().done); // false
console.log(gen2.next().done); // false
console.log(gen2.next().done); // false
console.log(gen2.next().done); // true
未完待续 ...
码字辛苦关注分享走一波
本文分享自微信公众号 - 像素摇摆(pxDance)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/4582094/blog/4388820