ES 6新语法
一、块级作用域绑定
回顾:使用var关键字定义变量
2. 定义时可以只声明不赋值
3. 定义之后可以随时修改变量的值
4. 变量声明会被提升
5. 可重复定义变量
6. 全局定义的变量会被作为全局对象(global/window)的属性
7. 在代码块中使用 var 关键字声明的变量不会被束缚在代码块中
if (true) { var foo, bar = 'abc';
} console.log(foo, bar)
1.1 使用let关键字定义变量
- 可以一次定义多个变量
- 定义时可以只声明不赋值
- 定义之后可以随时修改变量的值
- 使用 let 关键字定义的变量,变量声明不会被提升,因此我们需要先定义,后使用
- 在同一作用域下,不能重复定义同名变量
- 全局定义的变量会被作为全局对象(global/window)的属性
- 在代码块中使用 let 关键字声明的变量会被束缚在代码块中
// 0. 可以一次定义多个变量
// let foo = 123, bar = 'abc'; // 1. 定义时可以只声明不赋值
// let foo, bar; // 2. 定义之后可以随时修改变量的值
// let foo = 123, bar = 'abc';
// foo = 'abc';
// bar = 123;
// console.log(foo, bar); // 'abc' 123 // 3. 使用 let 关键字定义的变量,变量声明不会被提升,因此我们需要先定义,后使用
// console.log(foo); // foo is not undefined
// let foo = 123; // 4. 在同一作用域下,不能重复定义同名变量
// let foo = 123;
// let foo = 'abc'; // Identifier 'foo' has already been declared
// console.log(foo) // 5. 全局定义的变量会被作为全局对象(global/window)的属性 // 6. 在代码块中使用 let 关键字声明的变量会被束缚在代码块中
1.2 使用const关键字定义常量
1. 在使用 const 关键字声明常量时,必须要进行赋值(初始化)。
2. 常量一旦初始化后,就不能被修改。
3. 在同一作用域下,不能重复定义同名的常量。
4. 常量的声明不会被提升
5. 所有常量只在当前代码块内有效,一旦执行流到了代码块外,这些常量就会被立即销毁。
6.不在Windows下
<script> // 1. 在使用 const 关键字声明常量时,必须要进行赋值(初始化)。
// const foo = 123, bar = 'abc';
// const bar; // 抛出错误:Missing initializer in const declaration // 2. 常量一旦初始化后,就不能被修改。
// const foo = 123;
// foo = 'abc'; // 抛出错误:Assignment to constant variable. // 3. 在同一作用域下,不能重复定义同名的常量
// const foo = 123;
// const foo = 'abc'; // 抛出错误:Identifier 'foo' has already been declared // 4. 常量的声明不会被提升
// console.log(foo) // 抛出错误:foo is not defined
// const foo = 123; // 5. 所有常量只在当前代码块内有效,一旦执行流到了代码块外,这些常量就会被立即销毁。
// if (true) {
// const foo = 123;
// console.log(foo)
// }
// console.log(foo) // 抛出错误:foo is not defined </script>
1.3 模块字面量
- 插入数据
<script> let str1 = '你好,世界!';
let str2 = `Hello
World!`; // 可以直接换行
let str3 = `Hello ${str1} World!`; // 向模板中插入数据
console.log(str3)
// 使用场景: let students = [
{
name: '黄聪聪',
age: 20,
gender: '男'
},
{
name: '赵志刚',
age: 20,
gender: '男'
},
{
name: '江芦贵',
age: 20,
gender: '男'
}
]; let htmls = ''; students.forEach(function(student) {
htmls += `<tr>
<td>${student.name }</td>
<td>${student.age }</td>
<td>${student.gender}</td>
</tr>`;
}); console.log(htmls) </script>
1.4 展开操作符 (...)
- 给变量前面加三个点(...),就不再打印出数组格式了,(如控制台打印的一二行,第一行是使用展开操作符,第二行是没有使用展开操作符的)
- 使用场景:a.合并数组 b.复制数组
<script> let colors1 = ['red', 'green', 'blue']; // 使用展开操作符输出数组中的元素,类似于:console.log('red', 'green', 'blue')
console.log(...colors1)
console.log(colors1) //对象 // 使用场景一:合并数组,将 colors1 中的元素合并到 colors2 中
let colors2 = ['black', 'white', 'purple', ...colors1];
console.log(...colors2)
// 使用场景二:复制数组
let colors3 = [...colors2]
console.log(...colors3)
</script>
1.5 剩余操作符(又名不定参数)
不定参数:顾名思义,不知道还有多少个参数,剩余的统统接收,用来代替arguments;
- 函数中剩余操作符只能是函数中最后的一个形参,格式:...bar;
- 第一个实参会被赋值给第一个形参,剩余所有的实参都会被交给形参 bar,bar会自动变成一个数组;
<script>
// 实参 12 会被赋值给形参 a,剩余的所有实参都会被交给形参 bar,bar 会自动变成一个数组。
function foo (a, ...bar) {
console.log(a, bar)
} foo(12, 34, 45, 67, 89)
// 注意,剩余操作符只能应用到最后一个形参上,否则会抛出错误:Rest parameter must be last formal parameter
// 下面的写法是错误的:
// function foo (a, ...bar, b) {
// console.log(a, bar, b)
// } // foo(12, 34, 45, 67, 89)
</script>
二、解构
2.1解构的实用性
在 ECMAScript 5 或更早的版本中,从对象或数组中获取特定的数据并赋值给本地变量需要书写很多并且相似的代码。例如:
let options = {
repeat: true,
save: false
}; // 从对象中提取数据 let repeat = options.repeat,
save = options.save;
这段代码反复地提取在 options 上存储地属性值并将它们传递给同名的本地变量。虽然这些看起来不是那么复杂,不过想象一下如果你的一大批变量有着相同的需求,你就只能一个一个地赋值。而且,如果你需要从对象内部嵌套的结构来查找想要的数据,你极有可能为了一小块数据而访问了整个数据结构。
这也是 ECMAScript 6 给对象和数组添加解构的原因。当你想要把数据结构分解为更小的部分时,从这些部分中提取数据会更容易些。很多语言都能使用精简的语法来实现解构操作
2.2 对象解构 -解构赋值 ( { } 花括号)
- 声明的变量是同名变量,写在{}里即可,变量之间用“,”隔开就行;
- 声明的变量是非同名变量,在{}里写上" “:”+ “变量名” " ,变量之间用“,”隔开 例如:
let { firstName: first_name, lastName: last_name } = obj;
- 为变量指定默认值,当对象中没有某个属性时,直接写上变量的名字加“=”即可, 例如:
let { firstName, lastName, myAge = 20 } = obj;
let { firstName: first_name, lastName: last_name, myAge: my_age = 20} = obj;
- 为已有变量重新赋值,({}=obj),因为JS解析引擎不允许操作赋值符(=)左边出现花括号所以使用 "()"将整个赋值语句包起来,使他成为一个表达式
- 嵌套对象解构,(俄罗斯套娃,哈哈~~)例如:obj里面的phone里面的number赋值给同名变量number
let { phone: { number, brand } } = obj;
<script> const obj = {
firstName: '张康',
lastName: '尼古拉斯',
myAge: 30,
phone: {
number: 110,
brand: '魅族',
color: '黑色'
}
}; // 1. 使用对象解构初始化同名变量
// 告诉 obj 对象,把 firstName 属性的值赋值给同名变量 firstName,把 lastName 属性的值赋值给同名变量 lastName
// let { firstName, lastName } = obj;
// console.log(firstName, lastName) // '张康' '尼古拉斯' // 2. 使用对象解构初始化非同名变量
// 告诉 obj 对象,把 firstName 属性的值赋值给变量 first_name,把 lastName 属性的值赋值给变量 last_name // let { firstName: first_name, lastName: last_name } = obj;
// console.log(first_name, last_name) // '张康' '尼古拉斯' // 3. 为变量指定默认值
// 当 obj 对象中没有 myAge 属性时,变量 myAge 默认会被赋值为 20
// let { firstName, lastName, myAge = 20 } = obj;
// console.log(firstName, lastName, myAge) // '张康' '尼古拉斯' 20 // 当 obj 对象中没有 myAge 属性时,变量 my_age 默认会被赋值为 20
// let { firstName: first_name, lastName: last_name, myAge: my_age = 20} = obj;
// console.log(first_name, last_name, my_age) // '张康' '尼古拉斯' 20 // 4. 使用对象解构为已有变量重新赋值
// let first_name = '郭帅', last_name = '莱昂纳多'; 已有变量 // JS 解析引擎不允许赋值操作符(=)左边出现花括号,但是可以使用小括号将整个赋值语句包裹起来,这样一来,整个赋值语句就变成了一个表达式。
// ({ firstName: first_name, lastName: last_name } = obj); 重新赋值
// console.log(first_name, last_name) // '张康' '尼古拉斯' // 5. 嵌套对象解构
// 告诉 obj 对象,把 phone 对象中的 number 属性的值赋值给变量 number,把 phone 对象中的 brand 属性的值赋值给变量 brand
// let { phone: { number, brand } } = obj;
// console.log(number, brand) </script>
2.3 数组解构([ ]中括号)
- 数组解构初始化变量:数组中的元素会按照顺序赋值给变量
- 使用数组解构为变量重新赋值,[ ]=colors,
[ firstColor, secondColor ] = colors;我们
- 为变量指定默认值,当数组中没有第四个元素。而我们又需要第四个元素时,我们可以给变量赋值,但是当元素中有第四个元素,我们再给第四个元素赋值是无效的;
- 跳过数组中的指定元素,当我们不需要第二,三个元素,又需要第四个元素,中间可以用“ ,”隔开,
- 嵌套解构前面不需要的数据用 “ ,”隔开,嵌套的用“ [ ]”l来表示
<script> const colors = ['red', 'green', 'blue', 'orange', [ 'black', 'white' ] ]; // 1. 使用数组解构初始化变量
// 数组中的元素会按照顺序赋值给这 3 个变量
// let [ firstColor, secondColor, thirdColor ] = colors;
// console.log(firstColor, secondColor, thirdColor) // 2. 使用数组解构为变量重新赋值
// let firstColor = 'black', secondColor = 'white';
// 数组中的元素会按照顺序赋值给这 2 个变量
// [ firstColor, secondColor ] = colors;
// console.log(firstColor, secondColor) // 3. 为变量指定默认值
// 当数组中没有第 4 个元素时,变量 fourthColor 会被赋值成默认值 'pi但是nk'
// let [ firstColor, secondColor, thirdColor, fourthColor = 'pink' ] = colors;
// console.log(firstColor, secondColor, thirdColor, fourthColor) // 4. 跳过数组中的指定元素
// let [ firstColor, , ,fourthColor ] = colors;
// console.log(firstColor, fourthColor) // 5. 嵌套数组解构
let [,,,,[nestedFirstColor, nestedSecondColor]] = colors;
console.log(nestedFirstColor, nestedSecondColor) </script>
2.4 混合解构
<script> let node = {
type: 'Identifier',
name: 'foo',
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 4,
column: 4
}
},
range: [0, 3]
}; // 把 node 对象中 loc 对象的 start 属性的值赋值给变量 start,把 range 数组中第 2 个元素赋值给变量 endIndex
let { loc: { start }, range: [, endIndex] } = node; console.log(start, endIndex) </script>
2.5 参数解构
<script>
// 1. 以普通方式传参时,相当于把 obj 对象赋值给形参 student
// student = obj // function getFullName (student) {
// return student.firstName + ' · ' + student.lastName;
// } // let obj = {
// firstName: '刘旭凯',
// lastName: '约翰尼'
// }; // console.log(getFullName(obj)) // 2. 以对象解构的方式传参
// 把 obj 对象中的 firstName 属性的值传递给形参 firstName,把 lastName 属性的值传递给形参 lastName
// { firstName, lastName } = obj; // function getFullName ({ firstName, lastName }) {
// return firstName + ' · ' + lastName;
// } // let obj = {
// firstName: '刘旭凯',
// lastName: '约翰尼'
// }; // console.log(getFullName(obj)) // 3. 为形参指定默认值
// 当 obj 对象没有 lastName 属性时,形参 lastName 就会使用默认值 '尼古拉斯'
function getFullName ({ firstName, lastName = '尼古拉斯' }) {
return firstName + ' · ' + lastName;
} let obj = {
firstName: '刘旭凯',
lastName: '克里斯蒂安'
}; console.log(getFullName(obj)) </script>
三、函数
3.1 默认参数
ES5 语法中,为函数形参指定的默认值写法
<script>
// 1. 在 ES5 语法中,为函数形参指定默认值的写法:
// 写法一:
function foo (bar) {
bar = bar || 'abc';
console.log(bar)
}
foo('xyz') // 写法二:
function foo (bar) {
if (typeof bar === 'undefined') {
bar = 'abc';
}
console.log(bar)
} foo('xyz'); </script>
ES6中为函数形参指定默认值
a.除了为形参直接指定默认值以外,形参的默认值还可以是表达式,例如,timeout = 5 * 1000
b.在预编译阶段,形参表达式不会执行,只有在调函函数,并且没有为形参传递实参的情况下才执行
<script>
// 2. 使用 ES6 的语法为函数形参指定默认值
function post (url, data = {}, timeout = foo * 1000) {
console.log(arguments)
console.log(url, data, timeout)
} // post('xyz', {uname: 'zhangsan', upass: '123'}, 3000);
post('xyz', null, 3000); // 注意事项:
// 1> 除了为形参直接指定默认值以外,形参的默认值还可以是表达式,例如,timeout = 5 * 1000
// 2> 在预编译阶段,形参表达式不会执行,只有在调函函数,并且没有为形参传递实参的情况下才执行。 </script>
3.2 不定参数
<script> // 不定参数,使用剩余操作符接收剩余的实参,这些实参会被保存到一个不定参数(args)中
function foo (...args) {
return args.reduce(function (previousValue, currentValue) {
console.log(previousValue, currentValue)
return previousValue += currentValue;
})
} // 将上面的函数改成箭头函数的形式
var foo = (...args) => args.reduce((a, b) =>a += b) console.log(foo(1, 32, 34, 5, 6)) </script>
previousValue:上一个值
currentValue:当前的值
reduce()方法:
1.接收一个函数作为累加器,将数组元素计算为一个值(从左到右),
2.需要接收四个参数( 必需:1.acc 累加器 2.cur 当前值 ;可选:1.idx 当前索引 2 .src 源数组)
3.3 箭头函数
凡是以后遇到匿名函数都可以使用箭头函数
省略等号和function
- 如果形参数量为 0,则必须加上小括号。箭头后面的表达式的结果会被作为函数的返回值。
如果形参的数量为 1,则可以省略小括号。
如果形参数量大于 1,则不能省略小括号。
如果函数的执行体比较简单(直接量或表达式),可以省略大括号,箭头后面的直接量或表达式会被自动作为返回值
如果函数的执行体比较复杂,则不能省略大括号。
<script>
// 1. 形式一:
// var foo = function () {
// return 'Hello World!';
// }; // 如果形参数量为 0,则必须加上小括号。箭头后面的表达式的结果会被作为函数的返回值。
// var foo = () => {
// return 'Hello World!';
// } // 2. 形式二:
// var foo = function (greeting) {
// return greeting;
// } // 如果形参的数量为 1,则可以省略小括号。
// var foo = greeting => {
// return greeting;
// } // 3. 形式三:
// var foo = function (firstName, lastName) {
// return firstName + ' · ' + lastName;
// } // 如果形参数量大于 1,则不能省略小括号。
// var foo = (firstName, lastName) => {
// return firstName + ' · ' + lastName;
// } // 4. 形式四:
// var foo = function (a, b) {
// return a > b ? a : b;
// } // 如果函数的执行体比较简单(直接量或表达式),可以省略大括号,箭头后面的直接量或表达式会被自动作为返回值。
// var foo = (a, b) => a > b ? a : b; // 5. 形式五:
// var foo = function (a, b) {
// let max = a;
// if (b > a) {
// max = b;
// }
// return max;
// } // 如果函数的执行体比较复杂,则不能省略大括号。
// var foo = (a, b) => {
// let max = a;
// if (b > a) {
// max = b;
// }
// return max;
// }
// console.log(foo('贾树华', '尼古拉斯')) </script>
3.4 箭头函数没有this绑定
普通函数作用域中的 this 已经被绑定成 window 对象,因此当我们放问 this 时,直接在当前作用域下就能访问的到。
箭头函数的作用域中没有绑定 this,因此,当我们访问 this 时,会去上一层作用域中查找 this。
<script> // 普通函数作用域中的 this 已经被绑定成 window 对象,因此当我们放问 this 时,直接在当前作用域下就能访问的到。
// var foo = function () {
// console.log(this)
// return 'Hello World!';
// }; // 箭头函数的作用域中没有绑定 this,因此,当我们访问 this 时,会去上一层作用域中查找 this。
// var bar = () => {
// console.log(this)
// return 'Hello World!';
// }; // 示例,由于 sayName 箭头函数中没有绑定 this,因此我们访问 this 时,会去全局作用域中查找。
// 查找的结果是 this 指向 window,因此输出的 name 的值是 '常军辉' // var name = '常军辉';
// var obj = {
// name: '吕鑫洋',
// sayName: function () {
// // this = obj
// return this.name;
// }
// }; // console.log(obj.sayName()) </script>
3.5 箭头函数没有arguments绑定
<script> // 普通函数
var foo = function (greeting) {
console.log(arguments)
return greeting;
}; // 箭头函数中没有绑定 arguments 对象,因此下面的输入语句会报错:arguments is not defined
var bar = (greeting) => {
console.log(arguments)
return greeting;0
}; console.log(foo('Hello World!'))
console.log(bar('你好世界!')) </script>
3.6 箭头函数中不能手动绑定this
<script> // this 指向的对象
var obj = {
fullName: '谭文华'
}; // 1. 普通函数,可以使用 call() 方法改变函数中 this 的绑定
// var foo = function (greeting) {
// return this.fullName + '说:' + greeting;
// };
// console.log(foo.call(obj, 'Hello World!')) // 2. 箭头函数,不能使用 call() 方法改变函数中 this 的绑定,箭头函数中不能绑定 this。
var bar = (greeting) => {
return this.fullName + '说:' + greeting;
}; // 下面的代码不会报错,但是也不起作用
console.log(bar.call(obj, '你好世界!')) </script>
3.7 new.target
<script>
// 1. ECMAScript 5 中判断构造函数是否通过 new 关键字调用的写法
// function Person (fullName) {
// if (this instanceof Person) {
// this.fullName = fullName;
// } else {
// return new Person(fullName);
// }
// } // let student = Person('孟天乐') // 2. ECMASript 6 引入一个 new.target 属性,当我们使用 new 操作符调用构造函数时,new.target 属性的值为构造函数,否则为 undefined
// function Person (fullName) {
// if (typeof new.target !== 'undefined') {
// this.fullName = fullName;
// } else {
// throw new Error('必须通过 new 关键字来调用 Person。');
// }
// }
// let student = new Person('孟天乐');
// console.log(student) // 3. 除此之外,还可以检查 new.target 是否被某个特定构造函数所有调用。
// 例如,Person 构造函数中的 new.target 属性的值被限定为 Person
// function Person (fullName, age) {
// if (typeof new.target === Person) {
// this.fullName = fullName;
// this.age = age;
// } else {
// throw new Error('必须通过 new 关键字来调用 Person。');
// }
// } // function Dog (fullName, age) {
// Person.call(this, fullName, age)
// } // let dog = new Dog('HeHe', 3) // console.log(dog) // 4. 不能在函数外部使用 new.target,否则会报错
function Person () {
console.log(new.target)
} // 下面代码会抛出错误:new.target expression is not allowed here
// console.log(new.target) let student = new Person('崔景龙') </script>
四、对象
4.1 对象-属性初始值的简写
- 当一个对象的属性与本地变量同名时,不需要再写冒号和值,直接写属性名即可
<script> let fullName = '杨柯', age = 19; let obj = {
fullName: fullName,
age: age
}; // 1. 当一个对象的属性与本地变量同名时,不需要再写冒号和值,直接写属性名即可。
let obj = {
fullName,
age
}; </script>
4.2 对象方法的简写
<script> // 在 ES 5 中,如果为对象添加方法,必须要通过指定名称并完整定义函数来实现。
let obj = {
fullName: '杨柯',
sayName: function () {
return this.fullName;
}
}; // 在 ES 6 中,语法更简洁,取消了冒号和 function 关键字。如下所示:
let obj = {
fullName: '杨柯',
sayName () {
return this.fullName;
}
}; </script>
4.3 可计算属性名
<script> // 在对象字面量中使用方括号表示该属性名是可计算的,方括号中的内容会被计算求值,最终转化成一个字符串,该字符串就是最终的属性名。
let suffix = 'name'; let person = {
['first' + suffix]: '杨帅',
['last' + suffix]: '泰坦尼'
}; console.log(person) </script>
4.4 对象 新增的两个方法
<script> // 1. 在有些情况下,既是全等运算符比较出来的结果也是不正确的。例如,在下面两种情况下: // +0 和 -0 在 JS 解析引擎中被表示为两个完全不同的实体,而如果使用全等运算符(===)对两者进行比较,得到的结果是两者相等。
console.log(+0 == -0); // true
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false // NaN 和 NaN 在 JS 解析引擎中被表示为两个完全相同的实体,但是无论使用等于(==)还是全等(===),得到的结果都是 false。
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true // 在大多数情况下,Object.is() 方法的比较结果与全等运算符完全相同,唯一的区别在于 +0 和 -0 会被识别为不相等,NaN 和 NaN 会被识别为相等。 // 2. Object.assign() 方法可以接收任意数量的源对象(obj2,obj3),并按照指定的顺序将属性复制到接收对象(obj1)。
// 如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排外靠前的对象。 let obj1 = {
fullName: '陈世壮',
sayName () {
return this.fullName;
}
}; let obj2 = {
fullName: '任俊玖',
age: 20
}; let obj3 = {
fullName: '朱亚鹏',
gender: '男'
}; // 通过自定义方法实现了一个可以合并多个对象的方法,
// function mixin(receiver, ...suppliers) {
// suppliers.forEach(supplier => {
// Object.keys(supplier).forEach(key => {
// receiver[key] = supplier[key]
// })
// })
// return receiver;
// }
// console.log(mixin(obj1, obj2, obj3)) // 使用 ES6 新增 Object.assgin() 方法将多个对象的属性合并到第一个对象中。
// Object.assign(obj1, obj2, obj3); // console.log(obj1)
// console.log(obj2)
// console.log(obj3) 浅拷贝:(拷贝的是地址)引用类型拷贝的都是地址
</script>
4.5 重复的对象字面量属性
<script> // 对于每一组重复属性,都会选取最后一个取值。
let obj = {
fullName: '陈世壮',
fullName: '李忠易',
age: 18,
age: 20
}; console.log(obj.fullName); // '李忠易'
console.log(obj.age); // 20 </script>
4.6 自有属性枚举顺序
<script> // ES 5 中并没有定义对象属性的枚举顺序,有 JavaScript 引擎厂商自行决定。
// ES 6 中明确规定了对象的自有属性被枚举时的返回顺序。
// 自有属性枚举顺序的基本规则:
// 1. 所有数字按升序
// 2. 所有字符串按照它们被加入对象时的顺序排序 let obj = {
a: 1,
0: 2,
c: 3,
2: 4,
b: 5,
1: 6
}; console.log(Object.getOwnPropertyNames(obj)); // ["0", "1", "2", "a", "c", "b"] </script>
4.7 改变对象的属性
<script> let person = {
getGreeting () {
return 'Hello';
}
}; let dog = {
getGreeting () {
return 'woof';
}
}; // 使用 create() 方法将 person 对象作为原型对象
let friend = Object.create(person); // {}
console.log(friend.getGreeting()); // 'Hello'
console.log(Object.getPrototypeOf(friend) === person); // true // 使用 setPrototypeOf() 方法将 friend 对象的原型对象修改成 dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // 'Hello'
console.log(Object.getPrototypeOf(friend) === dog); // true </script>
4.8 使用super关键字访问原型对象(构造函数类)
<script> let person = {
getGreeting () {
return 'Hello';
}
}; let dog = {
getGreeting () {
return 'woof';
}
}; // let friend = {
// getGreeting () {
// return Object.getPrototypeOf(this).getGreeting.call(this) + ', hi';
// }
// }; // ES 6 引入了 super 关键字,super 指向当前对象的原型对象,实际上也就是 Object.getPrototypeOf(this) 的值,于是,上面的代码可以简化成如下形式:
let friend = {
getGreeting () {
return super.getGreeting() + ', hi';
}
}; // 使用 setPrototypeOf() 方法将 friend 对象的原型对象修改成 person
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // 'Hello'
console.log(Object.getPrototypeOf(friend) === person); // true // 使用 setPrototypeOf() 方法将 friend 对象的原型对象修改成 dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // 'Hello'
console.log(Object.getPrototypeOf(friend) === dog); // true </script>
五、类
5.1 ES5 中与类相似的结构
<script> // 构造函数类似于面向对象编程语言中类(class)
function PersonType(fullName) {
this.fullName = fullName;
} Object.defineProperty(PersonType.prototype, 'sayName', {
configurable: true,
enumerable: false,
writable: true,
value: function () {
return this.fullName;
}
}) let student1 = new PersonType('苑文浩'); console.log(student1) </script>
5.2 ES6 中新增类的概念
<script> // 使用 class 关键字定义 PersonClass 类
class PersonClass {
// PersonClass 类的构造函数,等价于构造函数 PersonType
// 当我们创建 PersonClass 的实例时,该方法会自动被调用。
constructor(fullName) {
this.fullName = fullName;
} // 为原型对象添加的方法,等于下面的代码:
// Object.defineProperty(PersonType.prototype, 'sayName', {
// configurable: true,
// enumerable: false,
// writable: true,
// value: function () {
// return this.fullName;
// }
// }) sayName() {
return this.fullName;
}
} let student1 = new PersonClass('刘旭凯'); console.log(student1) // 通过与之前的构造函数对比,类和构造函数的作用是一样的。
// 其实类的语法只是一种语法糖,实质上等价于一个自定义类型的构造函数。 </script>
5.3 类和构造函数的区别
5.4 类-构造函数的属性
<script> function PersonType(fullName) {
this.fullName = fullName;
// 实例对象的方法
this.greeting = function () {
return 'Hello World!';
};
} // 构造函数的方法
PersonType.create = function (fullName) {
return new PersonType(fullName)
}; // 原型对象的方法
Object.defineProperty(PersonType.prototype, 'sayName', {
configurable: true,
enumerable: false,
writable: true,
value: function () {
return this.fullName;
}
}); // 构造函数的方法,可以在不创建实例的情况下直接使用。
// PersonType.create('朱帅旗') // 在使用实例对象的属性时,必须要先创建实例对象。
// let student1 = new PersonType('苑文浩');
// console.log(student1.fullName)
// console.log(student1.sayName()) </script>
5.5 类-类的静态方法
<script> // 类的语法
class PersonClass { constructor(fullName) {
this.fullName = fullName;
// 实例对象的方法
this.greeting = function () {
return '你好世界!';
};
} // PersonClass 类的静态方法,等价于构造函数的方法
static create (fullName) {
return new PersonClass(fullName);
} // 原型对象的方法
sayName () {
return this.fullName;
} } // 类的静态方法,不用创建实例即可调用
PersonClass.create('张尊娟') // 在使用实例对象的属性时,必须要先创建实例对象。
let student1 = new PersonClass('张康');
console.log(student1.fullName)
console.log(student1.sayName()) </script>
5.6 类-构造函数的继承
<script> // 声明 Rectangle 构造函数
function Rectangle (width, height) {
this.width = width;
this.height = height;
} Object.defineProperty(Rectangle.prototype, 'getArea', {
enumerable: false,
configurable: true,
writable: true,
value: function () {
return '矩形面积为:' + this.width * this.height;
}
}); // 声明 Square 构造函数
function Square (size) {
Rectangle.call(this, size, size);
} // 让 Square 构造函数继承 Rectangle 构造函数
Square.prototype = Object.create(Rectangle.prototype, {
constructor: {
configurable: true,
enumerable: false,
writable: true,
value: Square
}
}); let square = new Square(20); console.log(square)
</script>
5.7 类-类的继承
<script> class Rectangle { constructor(width, height) {
this.width = width;
this.height = height;
} getArea () {
return '矩形面积为:' + this.width * this.height;
}
} // 让 Square 类继承 Rectangle 构类
class Square extends Rectangle { constructor (size) {
// 等价于 Rectangle.call(this, size, size)
super(size, size)
}
} let square1 = new Square(20) // square1.__proto__ --> obj.__proto__ --> Rectange.prototype.__proto__ --> Object.prototype console.log(square1) </script>
5.8 类-类的构造函数
<script> // 1. 当我们创建某个类的实例时,类的构造函数(constructor)会自动被调用。
class Rectangle { constructor(width, height) {
this.width = width;
this.height = height;
} getArea() {
return '矩形面积为:' + this.width * this.height;
}
} // 2. 如果子类中具有构造函数,则必须在构造函数中使用 super 方法调用父类的构造函数,并传入所需的参数。
class Square extends Rectangle {
constructor (size) {
super(size, size)
}
} // 3. 构造函数不是必需的,当我们不需要为实例对象初始化属性时,可以省略构造函数。
// 当创建类的实例时,会自动地为类添加一个默认的 constructor 构造函数,并在构造函数中使用 super() 方法调用父类的构造函数,并传入所有参数。
// class Square extends Rectangle { // } // JS 引擎在后台为我们做了哪些事情呢?下面的代码等价于上面的代码:
// class Square extends Rectangle {
// constructor (...args) {
// super(...args)
// }
// } let square1 = new Square(20) console.dir(Square)
console.log(square1) </script>
5.9 覆盖父类的方法
<script> //
class Rectangle { constructor(width, height) {
this.width = width;
this.height = height;
} getArea () {
return '矩形面积为:' + this.width * this.height;
}
} //
class Square extends Rectangle {
constructor (size) {
super(size, size)
} // 创建一个与父类同名的方法就可以覆盖父类的方法。
// 在顺着原型对象链查找方法时,该方法会被优先找到。
getArea () {
return '正方形的面积为:' + this.width * this.height;
}
} let square1 = new Square(20) console.log(square1)
console.log(square1.getArea()) </script>
5.10 调用父类的方法
<script> //
class Rectangle { constructor(width, height) {
this.width = width;
this.height = height;
} getArea () {
return '矩形面积为:' + this.width * this.height;
}
} //
class Square extends Rectangle {
constructor (size) {
super(size, size)
} // 在子类中使用 super 关键字就可以调用父类的方法
// Rectangle.prototype.getArea.call(this).replace('矩形', '正方形');
getArea () {
return super.getArea().replace('矩形', '正方形');
} } let square1 = new Square(20) console.log(square1)
console.log(square1.getArea()) </script>
5.11 继承类的静态方法
<script> class Animal {
constructor(name) {
this.name = name;
} static greeting () {
return 'Hello';
};
} class Dog extends Animal {
constructor (name) {
super(name)
}
} console.log(Animal.greeting()); // 父类的静态方法也会被子类继承,因此我们可以通过 Dog 调用父类的 greeting() 方法。
console.log(Dog.greeting()); </script>
5.12 ES5中自己实现的模块
<script> // 1、张三和李四在项目开发中遇到的问题如下: // 张三创建了一个 index.js 文件,内容如下:
let foo = 123; // 李四创建了一个 home.js 文件,内容如下:
let foo = 'abc'; // 当 index.js 和 home.js 文件同时引入到一个 index.html 文件中时,就会抛出错误。 // 2、ES 5 没有模块的概念,但是我们又需要类似于模块的功能,于是我们想了一系列的方式去自己实现模块的功能。
// 最常用的一种方式就是利用立即执行的函数表达式及其作用域实现模块的功能。例如: // 在 index.js 中创建模块 module1
let module1 = (function() { let foo = 123; function greeting () {
return 'Hello';
} return {
foo,
greeting
}; }()) // 在 home.js 中创建模块 module2
let module2 = (function() { let foo = 'abc'; function greeting () {
return 'Hello';
} return {
foo,
greeting
}; }()) // 我们只需要保证两个模块的名称不同即可,在使用模块中的函数或变量时,只需要在前面加上模块名即可,如下所示:
console.log(module1.foo)
console.log(module2.foo) </script>
5-13 ES6模块化规范
<body> <!-- 要想让浏览器把 module1.js 和 module2.js 当成两个模块去解析,你要为 script 标签添加 type="module" 属性 -->
<!-- 要想然下面的代码正确执行,不能使用文件协议(file://),也就是说不能直接使用浏览器打开该页面,需要将其放入服务器中,然后再打开。 -->
<script src="./13-module1.js" type="module"></script>
<script src="./14-module2.js" type="module"></script>
<script src="./15-module3.js" type="module"></script>
</body>
- module1.js
// 崔景龙 // 1. 模块私有的内容
// function multiply (x, y) {
// return x * y;
// } // 2. 模块导出的内容
// export function sum (x, y) {
// return x + y;
// } // export function substract (x, y) {
// return x - y;
// } // 3. 一次导出多个模块成员
function divide (x, y) {
return x / y;
} function remainder (x, y) {
return x % y;
} export { divide, remainder }; // 4. 为导出成员重命名
// export { divide as div, remainder as rem }; // 5. 默认导出,模块默认导出成员,其他模块导入该成员时,名称不能写到大括号中,并且无需与导出时的名称一致。
// export default function max (x, y) {
// return x > y ? x : y;
// } // 6. 每个模块只能有一个默认导出,因此下面的代码会抛出错误。
// export default function min (x, y) {
// return x < y ? x : y;
// }
- module2.js
// 刘旭凯 // 1. 从 module1.js 中导入 sum() 和 substract() 方法
// import { sum, substract } from './13-module1.js'; // 使用从 module1.js 中导入的方法
// console.log(sum(12, 48))
// console.log(substract(12, 48)) // 2. 导入重命名之后的模块成员
// 注意,必须要写成重命名之后的名称(div,rem),不能写成重命名之前的名称(divide,remainder)
// import { div, rem } from './13-module1.js';
// console.log(div(48, 12))
// console.log(rem(48, 12)) // 3. 导入其他模块默认导出成员
// 注意,名称不能写到大括号中,并且无需与导出时的名称一致。
// import abc from './13-module1.js';
// console.log(abc(12, 48)) // 4. 同时导入其他模块的默认导出的成员和普通导出的成员
// import abc, {sum, div} from './13-module1.js'
// console.log(abc(12, 48))
// console.log(sum(12, 48))
// console.log(div(12, 48)) // 5. 为导入的成员重命名
// 注意,重命名之后不能再使用之前的名称了,否则会抛出错误。
// import { substract as sub} from './13-module1.js';
// console.log(sub(12, 48))
// console.log(substract(12, 48)); // 抛出错误 // 6. 导入其他模块导出的所有成员
// import * as module1 from './13-module1.js'
// console.log(module1.sum(12, 34))
// console.log(module1.default(12, 34)) // 7. 导入没有导出任何成员的模块,目的是想让模块中的代码执行
// 注意,模块中的代码只在第一次被导入时执行。
// import './13-module1.js'; // 下面是一个简单的使用场景: // module1.js 文件中有如下代码:
// let age = 20; // export function getAge () {
// return age;
// } // export function setAge () {
// age++;
// } // module2.js 文件中的代码如下:
// import { setAge } from './13-module1.js';
// setAge(); // module3.js 文件中的代码如下:
// import './module2.js';
// import { getAge } from './module1.js';
// console.log(getAge()); // 21 // 8. 将导入的成员重命名之后再次导出
export { divide as div, remainder as rem } from './13-module1.js';
- module3.js
// 导入 module2.js 模块中导出的成员(对应 module2.js 文件中第8个示例)
import { div, rem } from './14-module2.js';
console.log(div(48, 12))
console.log(rem(48, 12))
ES 6新语法的更多相关文章
- Node.js 全栈开发(二)——ES 201x 新语法的使用之基础篇
在讲 ES 2015 新语法之前,先来说一下为什么叫 ES.JavaScript 是这门语言的名称,它有一个为它制定标准化的组织 European Computer Manufacturers Ass ...
- es6 新语法分享给爱前端的伙伴
相信es6大家并不陌生,那么我还是简单介绍一下es6,es是15年发布的,可以用babel转化成es5可以支持低端浏览器,es6是一种新的语法,流行的库基本都是基于es6开发的.所以小伙伴要掌握哦!而 ...
- [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性
回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...
- qt5中信号和槽的新语法
qt5中的连接 有下列几种方式可以连接到信号上 旧语法 qt5将继续支持旧的语法去连接,在QObject对象上定义信号和槽函数,及任何继承QObjec的对象(包含QWidget). connect(s ...
- Qt 5.0+ 中 connect 新语法与重载函数不兼容问题的解决方法,以及个人看法
Qt 5.0+ 版本提供了 connect 的新语法,相比之前的语法新语法可以提供编译期检查,使用也更方便.可是使用过程中发现一个小问题——当某个 signal 和成员函数是重载关系的时候,qmake ...
- .NET中那些所谓的新语法之一:自动属性、隐式类型、命名参数与自动初始化器
开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法,它们相对以前的老语法相比,做了很多的改进,简化了很多繁杂的代码格式,也大大减少了我们这些菜鸟码农的代码量.但是,在开心欢乐之余,我们也 ...
- .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法
开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...
- .NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式
开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Func/Predicate)和超爱的Lambda表达式.为了方便码农们,. ...
- .NET中那些所谓的新语法之四:标准查询运算符与LINQ
开篇:在上一篇中,我们了解了预定义委托与Lambda表达式等所谓的新语法,这一篇我们继续征程,看看标准查询运算符和LINQ.标准查询运算符是定义在System.Linq.Enumerable类中的50 ...
随机推荐
- 【开发记录】Linux常用命令记录(一)
记录CentOS下,常用的命令.有时候很难记得清楚,同时方便新来的同学查阅.(将不停的追加和完善) 1)查看CPU情况 cat /proc/cpuinfo |grep "model name ...
- HTML <input> placeholder 属性
css ::-webkit-input-placeholder { /* WebKit, Blink, Edge */ color: #909; } :-moz-placeholder { /* Mo ...
- HDU1847 Good Luck In CET4 Everybody
大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此.当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考 ...
- HDU1217-Arbitrage(乘法最短路)
Arbitrage is the use of discrepancies in currency exchange rates to transform one unit of a currency ...
- JPEG文件编/解码详解
JPEG文件编/解码详解(1) JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写.它由国际电话与电报咨询委员会CCITT(The Interna ...
- 《JavaScript 模式》知识点小抄本(上)
介绍 最近开始给自己每周订个学习任务,学习结果反馈为一篇文章的输出,做好学习记录. 这一周(02.25-03.03)我定的目标是<JavaScript 模式>的第七章学习一遍,学习结果的反 ...
- selenium处理弹窗
处理登录弹窗:https://www.cnblogs.com/TD1900/p/11938573.html #定位弹窗 ale = driver.switch_to.alert #处理方式 ale.a ...
- 【Oracle】ORA-12560: TNS: 协议适配器错误
问题现象: ORA-12560: TNS: 协议适配器错误 解决方法: 启动监听服务
- PHP7 break和continue的区别
break:结束当前 for,foreach,while,do-while 或者 switch 结构的执行. continue:在循环结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次 ...
- nginx 自启动设置
首先,在linux系统的/etc/init.d/目录下创建nginx文件,使用如下命令: 1 vim /etc/init.d/nginx 在脚本中添加如下命令: #!/bin/sh # # nginx ...