第三节:ES6用法之Function、Object、Class、Symbol
一. Function
1. 默认参数
(1). ES5的写法
{ function foo(x, y) { y = y || 'world' console.log(x, y) } foo('hello', 'imooc') foo('hello', 0) //输出hello world foo('hello') //输出hello world }
(2). ES6的写法
{ function foo(x, y = 'world') { console.log(x, y) } foo('hello', 0) //输出hello 0 foo('hello') //输出hello world }
PS: 函数参数是从左到右解析,如果没有默认值会被解析成 undefined,如果我们想让具体某个参数使用默认值,我们可以使用 undefined 进行赋值,如下段代码所示:
function f(x, y = 7, z = 42) { return x + y + z } console.log(f(1, undefined, 43)) // 51
在ES6中我们不仅可以给参数默认赋值具体的数值,同时参数赋值支持参数的逻辑运算进行赋值,如下段代码所示:
function f(x, y = 7, z = x + y) { return z * 0.5 } console.log(f(1, 7)) // 4 function ajax(url, { body = '', method = 'GET', headers = {} } = {}) { console.log(method) } ajax('http://www.imooc.com', { method: 'POST' })
(3). 获取参数的个数
A. ES5
在 ES5 中可以在函数体内使用 arguments 来判断。
function foo(a, b = 1, c) { console.log(arguments.length) } foo('a', 'b') //2
B. ES6
ES6 中不能再使用 arguments 来判断了,但可以借助 Function.length 来判断,但是,Function.length 是统计第一个默认参数前面的变量数
function foo(a, b = 1, c) { console.log(foo.length) } foo('a', 'b') // 1 function foo(a = 2, b = 1, c) { console.log(foo.length) } foo('a', 'b') // 0
2. Rest参数
(1). 在写函数的时候,部分情况我们不是很确定参数有多少个,比如求和运算,之前都是这么做的:
function sum() { let num = 0 Array.prototype.forEach.call(arguments, function(item) { num += item * 1 }) return num } console.log(sum(1, 2, 3)) // 6 console.log(sum(1, 2, 3, 4)) // 10
(2). 在 ES6 就不能这么写了,因为 arguments 的问题。现在需要这样写:
function sum(...nums) { let num = 0 nums.forEach(function(item) { num += item * 1 }) return num } console.log(sum(1, 2, 3)) // 6 console.log(sum(1, 2, 3, 4)) // 10
当然,Rest Parameter 也可以和其他参数一起来用,比如:
function sum(base, ...nums) { let num = base nums.forEach(function(item) { num += item * 1 }) return num } console.log(sum(30, 1, 2, 3)) // 36 console.log(sum(30, 1, 2, 3, 4)) // 40
注:arguments 不是数组,所以不能直接使用数组的原生 API 如 forEach,而 Rest Parameter 是数组,可以直接使用数组的原生 API。
3. 扩展运算符
Spread Operator 和 Rest Parameter 是形似但相反意义的操作符,简单的来说 Rest Parameter 是把不定的参数“收敛”到数组,而 Spread Operator 是把固定的数组内容“打散”到对应的参数。示例如下:
function sum(x = 1, y = 2, z = 3) { return x + y + z } console.log(sum(...[4])) // 9 console.log(sum(...[4, 5])) // 12 console.log(sum(...[4, 5, 6])) // 15
下面是没有之前的就写法
function sum(x = 1, y = 2, z = 3) { return x + y + z } console.log(sum.apply(null, [4])) // 9 console.log(sum.apply(null, [4, 5])) // 12 console.log(sum.apply(null, [4, 5, 6])) // 15
4. length属性 和 name属性
(1). length
函数指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。
function foo(x = 1, y = 2, z = 3) { console.log(x, y) } console.log(foo.length) // 0
(2). name属性
函数的name属性,返回该函数的函数名。
function foo() {} foo.name // "foo"
5. 箭头函数
详见:https://www.cnblogs.com/yaopengfei/p/14456993.html (需补充)
二. Object
1. 属性简洁表示法
(1). 在 ES6 之前 Object 的属性必须是 key-value 形式
let name = 'xiecheng' let age = 34 let obj = { name: name, age: age, study: function() { console.log(this.name + '正在学习') } }
(2). 在 ES6 之后是可以用简写的形式来表达:
let name = 'xiecheng' let age = 34 let obj = { name, age, study() { console.log(this.name + '正在学习') } }
2. 属性名表达式
在 ES6 可以直接用变量或者表达式来定义Object的 key。
let s = 'school' let obj = { foo: 'bar', [s]: 'imooc' } console.log(obj.foo); console.log(obj.school);
3. Object.is()
ES6之前的比较相等通常用== 或 ===,但在一些特殊情况下比较的不准确,比如:0和false,-0和+0,NaN和NaN比较。
console.log( // 0 == false // => true // 0 === false // => false // +0 === -0 // => true // NaN === NaN // => false // Object.is(0, false) // => false // Object.is(+0, -0) // => false // Object.is(NaN, NaN) // => true )
4. Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。
标准语法:Object.assign(target, ...sources)
const target = { a: 1, b: 2 } const source = { b: 4, c: 5 } const returnedTarget = Object.assign(target, source) console.log(target) // expected output: Object { a: 1, b: 4, c: 5 } console.log(returnedTarget) // expected output: Object { a: 1, b: 4, c: 5 } console.log(Object.is(target,returnedTarget)); //true
5. 对象的遍历方式
(1). for--in
for...in不能够用于遍历Array,for...in的作用是用于遍历对象的。
let obj = { name: 'xiecheng', age: 34, school: 'imooc' } for (let key in obj) { console.log(key, obj[key]); }
(2). Object.keys()
Object.keys()用于返回对象所有key组成的数组。
Object.keys(obj).forEach(key => {
console.log(key, obj[key])
})
(3). Object.getOwnPropertyNames()
Object.getOwnPropertyNames()用于返回对象所有key组成的数组。
Object.getOwnPropertyNames(obj).forEach(key => {
console.log(key, obj[key])
})
(4). Reflect.ownKeys()
Reflect.ownKeys()用于返回对象所有key组成的数组。
Reflect.ownKeys(obj).forEach(key => {
console.log(key, obj[key])
})
三. Class
1. 声明类
(1). ES6之前的两种写法
写法1
let Animal = function(type) { this.type = type this.walk = function() { console.log( `I am walking` ) } } let dog = new Animal('dog') let monkey = new Animal('monkey')
写法2
let Animal = function(type) { this.type = type } Animal.prototype.walk = function() { console.log( `I am walking` ) } let dog = new Animal('dog') let monkey = new Animal('monkey')
(2). ES6新写法
在 ES6 中把类的声明专业化了,不在用 function 的方式了
class Animal { constructor(type) { this.type = type } walk() { console.log( `I am walking` ) } } let dog = new Animal('dog') let monkey = new Animal('monkey') console.log(typeof Animal) //function
2. Setters & Getters
对于类中的属性,可以直接在 constructor 中通过 this 直接定义,还可以直接在类的顶层来定义。
class CustomHTMLElement { constructor(element) { this.element = element } get html() { return this.element.innerHTML } set html(value) { this.element.innerHTML = value } }
3. 静态方法
(1). ES6之前
let Animal = function(type) { this.type = type this.walk = function() { console.log( `I am walking` ) } } Animal.eat = function() { console.log( `I am eating` ) } // 调用静态方法 Animal.eat()
(2). ES6新写法
在 ES6 中使用 static 的标记是不是静态方法
class Animal { constructor(type) { this.type = type } walk() { console.log(`I am walking`) } static eat() { console.log(`I am eating`) } } // 调用静态方法 Animal.eat()
4. 继承
(1). ES5中实现继承
需要借助call方法和prototype原型来实现。
// 定义父类 let Animal = function(type) { this.type = type } // 定义方法 Animal.prototype.walk = function() { console.log( `I am walking` ) } // 定义静态方法 Animal.eat = function(food) { console.log( `I am eating` ) } // 定义子类 let Dog = function() { // 初始化父类 Animal.call(this, 'dog') this.run = function() { console.log('I can run') } } // 继承 Dog.prototype = Animal.prototype
(2). ES6实现继承的新写法
借助 extends 和 super 来实现。
class Animal { constructor(type) { this.type = type } walk() { console.log( `I am walking` ) } static eat() { console.log( `I am eating` ) } } class Dog extends Animal { constructor () { super('dog') } run () { console.log('I can run') } }
四. Symbol
ES6 引入了一种新的原始数据类型 Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
1. 声明方式
(1). 普通声明
let s = Symbol() typeof s // "symbol"
(2). 变量s就是一个独一无二的值。typeof的结果说明s是 Symbol 数据类型。既然是独一无二的,那么两个Symbol()就一定是不相等的:
let s1 = Symbol() let s2 = Symbol() console.log(s1) console.log(s2) console.log(s1 === s2) // false
注意:Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
(3). Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo') let s2 = Symbol('foo') console.log(s1) console.log(s2) console.log(s1 === s2) // false
2. Symbol.for() 和 Symbol.keyFor()
(1).Symbol.for()
接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
let s1 = Symbol.for('foo') let s2 = Symbol.for('foo') console.log(s1 === s2) // true
注意:Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。
(2). Symbol.keyFor()
方法返回一个已登记的 Symbol 类型值的key。
const s1 = Symbol('foo') console.log(Symbol.keyFor(s1)) // undefined const s2 = Symbol.for('foo') console.log(Symbol.keyFor(s2)) // foo
3. 作为属性名
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
例子:比如在一个班级中,可能会有同学名字相同的情况,这时候使用对象来描述学生信息的时候,如果直接使用学生姓名作为key会有有问题。
会覆盖:
const grade = { 张三: { address: 'xxx', tel: '111' }, 李四: { address: 'yyy', tel: '222' }, 李四: { address: 'zzz', tel: '333' }, } console.log(grade) // 只会保留最后一个李四
不会覆盖:
const stu1 = Symbol('李四') const stu2 = Symbol('李四') const grade = { [stu1]: { address: 'yyy', tel: '222' }, [stu2]: { address: 'zzz', tel: '333' }, } console.log(grade) console.log(grade[stu1]) console.log(grade[stu2])
4.属性遍历
代码分享
const sym = Symbol('imooc') class User { constructor(name) { this.name = name this[sym] = 'imooc.com' } getName() { return this.name + this[sym] } } const user = new User('xiecheng') console.log(user.getName()) for (let key in user) { console.log(key) } for (let key of Object.keys(user)) { console.log(key) } for (let key of Object.getOwnPropertySymbols(user)) { console.log(key) } for (let key of Reflect.ownKeys(user)) { console.log(key) }
5. 消除魔术字符串
(1). 背景
魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。
下面代码中,字符串Triangle和Circle就是魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。
function getArea(shape) { let area = 0 switch (shape) { case 'Triangle': area = 1 break case 'Circle': area = 2 break } return area } console.log(getArea('Triangle'))
(2). 解决
const shapeType = { triangle: Symbol(), circle: Symbol() } function getArea(shape) { let area = 0 switch (shape) { case shapeType.triangle: area = 1 break case shapeType.circle: area = 2 break } return area } console.log(getArea(shapeType.triangle))
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。