ES6语法~解构赋值、箭头函数、class类继承及属性方法、map、set、symbol、rest、new.target、 Object.entries......
2015年6月17日 ECMAScript 6发布正式版本
前面介绍基本语法, 后面为class用法及属性方法、set、symbol、rest等语法.
一、基本语法:
1、 定义变量:let
使用var 定义的变量没有{ }限制,在条件中定义的i,全局中都可以使用,造成变量污染,有变量提升预解析作用,只提升变量名,不提升值!降低js代码的可阅读性
相同作用域内,let不允许重复声明变量!!否则报错!!但可以更改变量值
使用let定义的变量:不会有变量提升,必须先定义后使用,否则先使用会报错: ReferenceError
在for循环条件中用let定义i,只能在{ }内使用,不会造成全局污染
2、 定义常量:const
const d =10; 不允许再为d重新赋值
const定义的常量必须在定义时给一个初始化的值,否则undefind无意义
利用const定义的常量,在相同作用域中,无法被重新赋值
const定义的常量有块级作用域{ } ,for循环中每一次循环都重新定义了一个作用域
3、 解构赋值:
定义:所谓的解构赋值,就是把某个对象中的属性,当作变量,给解放出来,今后就能够当作变量直接使用了;
语法:
let user = { name: “zs”, age : 20, genter: ‘男’ }
let { name } = user --à 把name解构出来做变量使用
let { name: username , age: userage } = user -à这时name就不存在了,取而代之的是username, 且username/userage无法再被重新赋值!
使用时:直接用username、userage---àconsole.log(username) //zs
4、 箭头函数--(只针对改造匿名函数)
(形参体列表)=> { 函数体代码 }
<1> 特点:
箭头函数,本质上就是一个匿名函数;
箭头函数的特性: 箭头函数内部的 this, 永远和箭头函数外部的 this 保持一致;
btn.onclick = function() {
setTimeout(() => { //原本定时器内部的this指向window
console.log(this) //<button id="btn">点击<button>
this.style.backgroundColor = "red"
}, 1000)
}
<2> 箭头函数的三个变体:
正规:去掉function、函数名:
var 函数名 = (参数1,…) => { }
函数名(参数1,…)----调用
如:var add = (x, y) => { return x+y }
add(1, 2)
① 变体1:如果箭头函数,左侧的形参列表中,只有一个参数,则,左侧小括号可以省略;
var add = x => { return x + 10}
console.log( add(1) ) //11
② 变体2:如果右侧函数体中,只有一行代码,则右侧的 { } 和return可以省略;
var add = (x , y) => x + y
console.log(add(1, 2)) //3
③ 变体3:如果箭头函数左侧 只有一个形参,而且右侧只有一行代码,则两边的 () 和 {} 都可以省略
var add = x => x + 10
console.log(add(1)) //11
二、其他语法
1、Class 的基本语法
constructor(x, y) { this.x = x; this.y = y; } toAdd() { return this.x + this.y }}上面代码定义了一个“类”,可以看到里面有一个`constructor`方法,这就是构造方法,而`this`关键字则代表实例对象。也就是说,ES5 的构造函数`Point`,对应 ES6 的`Point`类的构造方法。方法之间不需要逗号分隔,加了会报错。使用时:```javascriptlet b = new Point(1, 2)console.log(b.toAdd()) //3typeof Point // "function"Point === Point.prototype.constructor // true
构造函数的prototype
属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype
属性上面。
第一段代码相当于:Point.prototype = { constructor() {}, toAdd() {},};
在类的实例上面调用方法,其实就是调用原型上的方法。
constructor 方法
constructor方法是类的默认方法,通过
new命令生成对象实例时,自动调用该方法。一个类必须有
constructor方法,如果没有显式定义,一个空的
constructor`方法会被默认添加。
class Point {}// 等同于class Point { constructor() {}}
与 ES5 一样,类的所有实例共享一个原型对象。
var p1 = new Point(2,3);var p2 = new Point(3,2);p1.__proto__ === p2.__proto__//true
Class 表达式
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me { getClassName() { return Me.name; }};
需要注意的是,这个类的名字是Me
,但是Me
只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用MyClass
引用。
如果类的内部没用到Me的话,可以省略Me
,也就是可以写成下面的形式。
const MyClass = class { /* ... */ };
采用 Class 表达式,可以写出立即执行的 Class。
自执行类:let person = new class { //直接new调用自己 constructor(name) { this.name = name; } sayName() { console.log(this.name); }}('张三');person.sayName(); //张三
上面代码中,person
是一个立即执行的类的实例。
注意点
(1)严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict
指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。
(2)不存在提升
类不存在变量提升(hoist),这一点与 ES5 完全不同。
new Foo(); // ReferenceErrorclass Foo {}
这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。
(3)name 属性
由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class
继承,包括name
属性。
class Point {}Point.name // "Point"
name
属性总是返回紧跟在class
关键字后面的类名。
(4)this 的指向
类的方法内部如果含有this
,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法(单独解放出来使用),很可能报错。
class Logger {
printName(name = 'there') {
this.print(Hello ${name}
);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName(); // 单独使用报错:TypeError: Cannot read property 'print' of undefined
logger.printName(). //Hello there. 实例调用不会报错
如果需要单独使用它:
解决办法:
1、箭头函数:
class Person {
printName = (name = 'aaa') => {
this.print(hello ${name}
)
}
print(text) {
console.log(text)
}
}
let a = new Person()
a.printName('zhang') //hello zhang
2、绑定this
constructor() {
this.printName = this.printName.bind(this) //this就是Person
}
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接通过类直接来调用,这就称为“静态方法”。
class Foo { static classMethod() { return 'hello'; }}Foo.classMethod() // 'hello'var foo = new Foo(); //这是实例属性foo.classMethod() // TypeError: foo.classMethod is not a function
注意,如果静态方法包含this
关键字,这个this
指的是类,而不是实例。
1、静态方法调用与实例方法调用的区别:
只要没被static定义的,实例都可以直接访问到!
class Foo { static bar() { this.baz(); } static baz() { //静态方法调用的也必须是static声明的方法 console.log('hello'); } baz() { console.log('world'); }}Foo.bar() // hello 静态调用,this指向类Foolet a = new Foo()a.baz() //world 实例调用a.bar() // 报错:a.bar is not a function
2、父类的静态方法,可以被子类继承。
class Foo { static classMethod() { return 'hello'; }}class Bar extends Foo {}Bar.classMethod() // 'hello'
3、静态方法也是可以从super
对象上调用的。
class Foo { static classMethod() { return 'hello'; }}class Bar extends Foo { static classMethod() { return super.classMethod() + ', too'; }}Bar.classMethod() // "hello, too"
实例属性的新写法
实例属性除了定义在constructor()
方法里面的this
上面,也可以定义在类的最顶层。
所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。
class IncreasingCounter { _count = 0; //直接把属性定义在最顶层,不需在constructor中this._count = 0 get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; }}
静态属性
静态属性指的是 Class 本身的属性,写法是在实例属性法的前面,加上static
关键字。
class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 }}
new.target 属性
ES6 为new
命令引入了一个new.target
属性,该属性一般用在构造函数之中,返回new
命令作用于的那个构造函数。如果构造函数不是通过new
命令或Reflect.construct()
调用的,new.target
会返回undefined
,因此这个属性可以用来确定构造函数是怎么调用的。
function Person(name) { if (new.target !== undefined) { this.name = name; } else { throw new Error('必须使用 new 命令生成实例'); }}// 另一种写法function Person(name) { if (new.target === Person) { this.name = name; } else { throw new Error('必须使用 new 命令生成实例'); }}var person = new Person('张三'); // 正确var notAPerson = Person.call(person, '张三'); // 报错
需要注意的是,子类继承父类时,new.target
会返回子类。
class A { constructor() { console.log(new.target.name); }}class B extends A { constructor() { super(); }}new A() // Anew B() // B
new.target
指向当前正在执行的函数。可以看到,在super()
执行时,它指向的是子类B
的构造函数,而不是父类A
的构造函数。也就是说,super()
内部的this
指向的是B
。
利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。
2、Class 的继承extends
Class 可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
(1)子类通过extends
关键字,继承了父类的所有属性和方法(包括静态方法)
在子类的构造函数中,只有调用super
之后,才可以使用this
关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super
方法才能调用父类实例。
class Point { constructor(x, y) { this.x = x; this.y = y; }}class ColorPoint extends Point { constructor(x, y, color) { this.color = color; // ReferenceError super(x, y); this.color = color; // 正确 }}
上面代码中,子类的constructor
方法没有调用super
之前,就使用this
关键字,结果报错,而放在super
方法之后就是正确的。
(2)实例对象即是子类实例,也是父类实例
let cp = new ColorPoint(25, 8, 'green');cp instanceof ColorPoint // truecp instanceof Point // true
上面代码中,实例对象cp
同时是ColorPoint
和Point
两个类的实例,这与 ES5 的行为完全一致。
Object.getPrototypeOf()
Object.getPrototypeOf
方法可以用来从子类上获取父类。
Object.getPrototypeOf(ColorPoint) === Point // true
因此,可以使用这个方法判断,一个类是否继承了另一个类。
super 关键字
super
这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
(1)第一种情况,super
作为函数调用时,代表调用父类的构造函数。ES6 要求,子类的构造函数必须执行一次super
函数,否则报错。
class A {}class B extends A { constructor() { super(); }}
注意,super
虽然代表了父类A
的构造函数,但是返回的是子类B
的实例,即super
内部的this
指的是B
的实例,因此super()
在这里相当于A.prototype.constructor.call(this)
。
(2)super()
内部的this
指向的是子类实例而不是父类!!。
而ES6 规定,在子类普通方法中通过super
调用父类的方法时,方法内部的this
也指向当前的子类实例。
由于this
指向子类实例,所以如果通过super
对某个属性赋值,这时super
就是this
,赋值的属性会变成子类实例的属性。
class A { constructor() { this.x = 1; }}class B extends A { constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); // undefined console.log(this.x); // 3 }}let b = new B();
上面代码中,super.x
赋值为3
,这时等同于对this.x
赋值为3
。而当读取super.x
的时候,读的是A.prototype.x
,所以返回undefined
。
(3)super( ) 作为函数时,super()
只能用在子类的构造函数之中,用在其他地方就会报错。不能用在方法中!
class A {}class B extends A { m() { super(); // 报错 } super(); //报错}
上面代码中,super()
用在B
类的m
方法之中,或直接写,就会造成句法错误。
(4) super
作为对象时,用在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
1⃣️ 这里需要注意,由于super
指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super
调用的。
super在普通方法之中,指向
A.prototype,所以
super.p()就相当于
A.prototype.p()`。
class A { constructor() { this.p = 2; }}class B extends A { get m() { return super.p; }}let b = new B();b.m // undefined
上面代码中,p
是父类A
实例的属性,super.p
就引用不到它。
如果属性定义在父类的原型对象上,super
就可以取到。
class A {}A.prototype.x = 2;class B extends A { constructor() { super(); console.log(super.x) // 2 }}let b = new B();
2⃣️如果super
作为对象,用在静态方法之中,这时super
将指向父类,`在普通方法之中指向父类的原型对象。
class Parent { static myMethod(msg) { console.log('static', msg); } myMethod(msg) { console.log('instance', msg); }}class Child extends Parent { static myMethod(msg) { super.myMethod(msg); } myMethod(msg) { super.myMethod(msg); }}Child.myMethod(1); // static 1var child = new Child();child.myMethod(2); // instance 2
3⃣️在子类的静态方法中通过super
调用父类的方法时,方法内部的this
指向当前的子类,而不是子类的实例。
class A { constructor() { this.x = 1; } static print() { console.log(this.x); }}class B extends A { static x = 4; //子类静态属性 constructor() { super(); this.x = 2; } static m() { super.print(); }}B.x = 3; 子类静态属性B.m() // 3
4⃣️使用super
的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
class A {}class B extends A { constructor() { super(); console.log(super.valueOf() instanceof B); // true }}let b = new B();
上面代码中,super.valueOf()
表明super
是一个对象,因此就不会报错。同时,由于super
使得this
指向B
的实例,所以super.valueOf()
返回的是一个B
的实例。
instanceof的普通的用法,obj instanceof Object 检测Object.prototype是否存在于参数obj的原型链上。
Person的原型在p的原型链中
function Person(){};var p =new Person();console.log(p instanceof Person);//true
类的 prototype 属性和proto属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__
属性,指向对应的构造函数的prototype
属性。Class 作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链。
(1)子类的__proto__
属性,表示构造函数的继承,总是指向父类。
(2)子类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性。
class A {}class B extends A {}B.__proto__ === A // trueB.prototype.__proto__ === A.prototype // true
上面代码中,子类B
的__proto__
属性指向父类A
,子类B
的prototype
属性的__proto__
属性指向父类A
的prototype
属性。
3、Symbol—js的第七种数据类型
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。
它是 JavaScript 语言的第七种数据类型,前六种是:undefined
、null
、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol 值通过Symbol
函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
let s = Symbol();typeof s// "symbol"
注意,Symbol
函数前不能使用new
命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
let s1 = Symbol('foo');let s2 = Symbol('bar');s1 // Symbol(foo)s2 // Symbol(bar)s1.toString() // "Symbol(foo)"s2.toString() // "Symbol(bar)"
上面代码中,s1
和s2
是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol()
,不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
注意,Symbol
函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol
函数的返回值是不相等的。
所以无论有没有参数,无论参数是否相等,Symbol值都是独一无二的。
// 没有参数的情况let s1 = Symbol();let s2 = Symbol();s1 === s2 // false// 有参数的情况let s1 = Symbol('foo');let s2 = Symbol('foo');s1 === s2 // false
Symbol 值不能与其他类型的值进行运算,会报错。
但是,Symbol 值可以显式转为字符串。
另外,Symbol 值也可以转为布尔值,但是不能转为数值。
let sym = Symbol();Boolean(sym) // true!sym // falseif (sym) { // ...}Number(sym) // TypeErrorsym + 2 // TypeError
作为属性名的 Symbol
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let mySymbol = Symbol();// 第一种写法let a = {};a[mySymbol] = 'Hello!';// 第二种写法let a = { [mySymbol]: 'Hello!'};// 第三种写法let a = {};Object.defineProperty(a, mySymbol, { value: 'Hello!' });// 以上写法都得到同样结果a[mySymbol] // "Hello!"
Symbol 值作为对象属性名时,不能用点运算符。
在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
4、Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set 数据结构。
可用于数组去重!
const s = new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));for (let i of s) { console.log(i);}// 2 3 5 4
上面代码通过add()
方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值
Set`函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
// 例一const set = new Set([1, 2, 3, 4, 4]); // set返回一个对象Set{1,2,3,4}、size[...set] // 扩展运算符把对象展开,放进数组。// [1, 2, 3, 4]// 例二const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);items.size // 5 // 返回对象中有5个成员// 例三const set = new Set(document.querySelectorAll('div'));set.size // 56// 类似于const set = new Set();document .querySelectorAll('div') .forEach(div => set.add(div));set.size // 56
上面代码也展示了一种去除数组重复成员的方法。
// 去除数组的重复成员[...new Set(array)] // 扩展运算符把对象展开放进数组(利用set去重)
上面的方法也可以用于,去除字符串里面的重复字符。
[...new Set('ababbc')].join('')// "abc"
向 Set 加入值的时候,不会发生类型转换,所以5
和"5"
是两个不同的值。Set内部判断使用精确判断(===)
let set = new Set();let a = NaN;let b = NaN;set.add(a);set.add(b);set // Set {NaN}
上面代码向 Set 实例添加了两个NaN
,但是只能加入一个。这表明,在 Set 内部,两个NaN
是相等。
另外,两个对象总是不相等的。两个空对象不相等,所以它们被视为两个值。
let set = new Set();set.add({});set.size // 1set.add({});set.size // 2
属性方法:
Set 结构的实例有以下属性。
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
add(value)
:添加某个值,返回 Set 结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set
的成员。clear()
:清除所有成员,没有返回值。
遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
keys()
:返回键名的遍历器values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员
由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。
上面遍历相当于:
Object.keys(). Object.values()、 Object.entries()
可以省略values
方法,直接用for...of
循环遍历 Set。
let set = new Set(['red', 'green', 'blue']);for (let x of set) { console.log(x);}// red// green// blue
扩展运算符(...
)内部使用for...of
循环,所以也可以用于 Set 结构。
let set = new Set(['red', 'green', 'blue']);let arr = [...set];// ['red', 'green', 'blue']
而且,数组的map
和filter
方法也可以间接用于 Set 了。
filter()返回新数组,不改变原数组!!
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);let b = new Set([4, 3, 2]);// 并集let union = new Set([...a, ...b]);// Set {1, 2, 3, 4}// 交集 let intersect = new Set([...a].filter(x => b.has(x)));// set {2, 3} 在a里筛选出b里也有的// 差集let difference = new Set([...a].filter(x => !b.has(x)));// Set {1} 在a里筛选出b里没有的
如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from
方法。
// 方法一let set = new Set([1, 2, 3]);set = new Set([...set].map(val => val * 2));// set的值是2, 4, 6// 方法二let set = new Set([1, 2, 3]);set = new Set(Array.from(set, val => val * 2));// set的值是2, 4, 6
上面代码提供了两种方法,直接在遍历操作中改变原来的 Set 结构。
5、map
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
Set的属性和方法,Map也适用!
注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。
const map = new Map();map.set(['a'], 555);map.get(['a']) // undefined
上面代码的set
和get
方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get
方法无法读取该键,返回undefined
。
同理,同样的值的两个实例,在 Map 结构中被视为两个键。
const map = new Map();const k1 = ['a'];const k2 = ['a'];map.set(k1, 111).set(k2, 222);map.get(k1) // 111map.get(k2) // 222
上面代码中,变量k1
和k2
的值是一样的,但是它们在 Map 结构中被视为两个键。
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0
和-0
就是一个键,布尔值true
和字符串true
则是两个不同的键。另外,undefined
和null
也是两个不同的键。虽然NaN
不严格相等于自身,但 Map 将其视为同一个键。
6、Number.isInteger() 用来判断一个值是否为整数。
7、 Object.entries()把对象的 每个key和value放进一个数组
ES6,引入了跟Object.keys配套的Object.values和Object.entries。
let {keys, values, entries} = Object;let obj = { a: 1, b: 2, c: 3 };for (let key of keys(obj)) { console.log(key); // 'a', 'b', 'c'}for (let value of values(obj)) { console.log(value); // 1, 2, 3}for (let [key, value] of entries(obj)) {console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]}
8、rest
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。这样就不需要使用arguments
对象了。
function add(...values) { // 把传入的参数,放进数组中。 let sum = 0; for (var val of values) { sum += val; } return sum;}add(2, 5, 3) // 10
下面是一个 rest 参数代替arguments
变量的例子。
// arguments变量的写法function sortNumbers() { return Array.prototype.slice.call(arguments).sort();}// rest参数的写法const sortNumbers = (...numbers) => numbers.sort();
上面代码的两种写法,比较后可以发现,rest 参数的写法更自然也更简洁。
arguments
对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call
先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
Array.prototype.slice.call ( obj ) ; 可以将伪数组转换为数组!!
下面是一个利用 rest 参数改写数组push
方法的例子:
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); });}var a = [];push(a, 1, 2, 3)
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
转载于:https://www.cnblogs.com/zixuan00/p/10381325.html
ES6语法~解构赋值、箭头函数、class类继承及属性方法、map、set、symbol、rest、new.target、 Object.entries......相关推荐
- ES6语法---解构赋值
解构赋值概念 按照一定的模式,从数组和对象中提取值,对变量进行赋值,就被称为解构. 目的是为了提高效率,使用起来更加方便. 以下的各个说明,我会类比着ES5去解释,希望能帮到小伙伴们. 数组解构 正常 ...
- ES6新语法--解构赋值
对象解构赋值 ※很重要 /*** 对象解构赋值:获取元素中属性的值,然后赋值给变量*///声明一个对象 let obj = {name:'chen',age:38,gender:'man',score ...
- ES6-4/5 解构赋值、函数默认值、数组解构、对象解构
ES-4 解构赋值.函数默认值.数组解构.对象解构 ES-5 隐式转换.函数参数解构.解构本质.()用法 一 解构赋值 1 虚值 含义:在Boolean转换结果为假的值falsy 2 函数默认值 ES ...
- ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?
本文来源于知乎上的一个提问. 为了程序的易读性,我们会使用 ES6 的解构赋值: function f({a,b}){} f({a:1,b:2}); 这个例子的函数调用中,会真的产生一个对象吗?如果会 ...
- ES6常用解构赋值有哪几种?
ES6常用解构赋值有哪几种? a.数组的解构赋值 //数组解析赋值,模式匹配 {let [a, b, c] = [1, 2, 3];console.log("模式匹配");cons ...
- 2021-02-24let和const,变量的解构赋值,函数扩展
文章目录 let 1.ES6简介 2.let关键字 2.1 不存在变量提升 2.2 暂时性死区 2.3 不允许重复声明 3.块级作用域 3.1 为什么需要块级作用域 缺点1:内部变量可能会覆盖外层的变 ...
- es6学习 -- 解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 以前,为变量赋值,只能直接指定值. let a = 1; let b = 2; let c ...
- ES6中解构赋值深入解读
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构 1.数组的解构赋值 let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail ...
- ES6:解构赋值及对象方法
解构赋值 对象解构 起别名:通过:来进行取名 //对象解构赋值 var obj = { uname: "张三", age: 21, sex: "男" } // ...
最新文章
- redis学习笔记---redis的哨兵Sentinel
- Order笔记-数据库创建
- Python基础-re模块
- tensorflow随笔-队列(1)
- python中向量长度_python中向量指的是什么意思
- 打通NTFS权限 文件共享各取所需
- 基于android的简单网页_成都APP开发:APP原生和网页开发有什么区别?
- C#窗体excel与dbf的导入导出
- 原生js 修改html,原生JS改变HTML内容
- 怎么做一个专业的软件安装包?
- 关于Android Pie(Android 9.0),你想知道的都在这了
- mysql80004005错误_常见的80004005错误及其解决方法
- vsFTP 基础及实战
- level 1与level 2的区别
- 修改mariadb遇到的问题
- 详解C语言中sizeof的使用
- Python|标识符命名规则
- 用python的gui界面设计签名_Python GUI--Tkinter简单实现个性签名设计
- 几种同源关系:直系同源、旁系同源和异同源
- 计算机中丢失d3dx9.30,win10找不到d3dx9_30.dll怎么修复_win10系统 d3dx9_30.dll丢失如何解决...
热门文章
- Bloomberg开源面向OCaml的JavaScript后端BuckleScript
- 取eclipse console 打印字符串,判断日志是否有异常
- 基于HTML5的Google水下搜索
- (C++)从字符串中取出整形、浮点型和字符串
- ansys大变形开关要不要打开_ANSYS不收敛问题的解决办法
- 参加web前端培训需要注意什么
- 【Web前端培训基础知识】ES5及ES6this详解
- 学java为什么要报java培训班?
- 在UIWindow上加类似于“回到顶部”的按钮
- 切版网上线,启用qieban.cn