14.ES6知识点详细解析
00:22:00
const names = ["abc","cba","nba"]
for (const i = 0; i < names.length; i++) {
console.log(names[i])
}
分解上面的代码块为:
{
const i = 0
console.log(names[i])
}
{
const i = 上面的 i++ 后赋值到这边;是因为常量 i 不能进行++ 操作,【所以会报错!】
console.log(names[i])
}
用 for … of 方式遍历数组(对象),下面这样是可以的
for (const item of names) {
console.log(item)
}
00:27:00
在ES6中,我们还有一个概念称之为暂时性死区:
· 它表达的意思是在一个代码中,使用let、const声明的变量,在声明之前,变量都是不可以访问的;
· 我们将这种现象称之为temporal dead zone (暂时性死区,TDZ);
var foo = "foo"
if (true) {
console.log(foo) //报错【社区叫法“暂时性死区”】(当然,如果下一行不写,不会报错)
let foo = "abc"
}
var、let、const的选择
那么在开发中,我们到底应该选择使用哪一种方式来定义我们的变量呢?
对于var的使用:
· 我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些历史遗留问题;
· 其实是JavaScript在设计之初的一种语言缺陷;
· 当然目前市场上也在利用这种缺陷出一系列的面试题,来考察大家对JavaScript语言本身以及底层的理解;
· 但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量了;
【因为:构建工具的基础上创建项目\开发项目 webpack/vite/rollup babel => ES6 -> ES5】
对于let、const:
· 对于let和const来说,是目前开发中推荐使用的;
· 我们会优先推荐使用const,这样可以保证数据的安全性不会被随意的篡改;
· 只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;
· 这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范;
ES6的其他知识点:
// ES6之前拼接字符串和其他标识符
const name ="why"
const age = 18
const height=1.88
console.log("my name is" + name + ", age is " + age + ", height is " + height)
// ES6提供模板字符串 `` 【键盘Tab 上方】
const message = `my name is ${name}, age is ${age}, height is ${height}`
console.log(message)
const info = `age double is ${age * 2}`
console.log(info)
function doubleAge() {
return age *2
}
const info2 = `double age is ${doubleAge()}`
console.log(info2)
00:48:00
function foo(m, n, x) {
console.log(m, n, x, '---------')
}
//另外调用函数的方式:标签模块字符串
//foo``
//foo`Hello World`
const name = "why"
const age = 18
foo`Hello${name}Wo${age}rld`
输出:['Hello', 'Wo', 'rld'] 'why' 18 '---------'
说明:
// 第一个参数依然是模块字符串中整个字符串,只是被切成多块,放到了一个数组中
// 第二个参数是模块字符串中,第一个${}
题外话:
react开发【框架代码中就有运用到“标签模板字符串”】
all in js: html css js in js 即:在js中去编写:html, css, js
css in js第三方库
styled-components
Vue开发
template 即:html形式
script
style
// ES5以及之前给参数默认值
/*
缺点:
1. 写起来很麻烦,并且代码的阅读性是比较差
2. 这种写法是有bug
*/
function foo(m, n) {
m = m || "aaa"
n = n || "bbb"
console.log(m, n)
}
foo()
foo(0, "")
//ES5要写这样才没有bug
function foo() {
var m = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "aaa";
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "bbb";
console.log(m, n)
}
// 1.ES6可以给函数参数提供默认值
function foo(m = "aaa", n = "bbb") {
console.log(m, n)
}
// 2.对象参数和默认值以及解构
function printInfo({name, age} = {name: "why", age: 18}) {
console.log(name,age)
}
printInfo() // why 18
printInfo({name: "kobe", age: 40}) // kobe 40
//上面“对象参数和默认值”的另一种写法:
function printInfo({name = "why",age = 18} = {}) {
console.log(name, age)
}
// 3.有默认值的形参最好放到最后
function bar(x, y, z=30) {
console.log(x, y, z)
}
// 4.有默认值的函数的length属性
function baz(x, y, z = 30, m, n) {
console.log(x, y, z, m, n)
}
console.log(baz.length) //2 ,默认值前面有几个参数,则长度为几个
ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中:
· 如果最后一个参数是“...”为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组;
那么剩余参数和arguments有什么区别呢?
· 剩余参数只包含那些没有对应形参的实参,而arguments对象包含了传给函数的所有实参;
· arguments对象不是一个真正的数组,而rest参数是一个真正的数组,可以进行数组的所有操作;
· arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供并且希望以此来替代arguments的;
// 函数的剩余参数
function foo(m, n, ...args) {
console.log(m, n) //20 30
console.log(args) //[40, 50, 60]
console.log(arguments) //[20, 30, 40, 50, 60, callee: (...), Symbol(Symbol.iterator): ƒ]
}
foo(20, 30, 40, 50, 60)
// rest paramaters【...args】必须放到最后
// 不然会报错:Rest parameter must be last formal parameter
题外话:
//下面这样写可以,但不推荐
function foo(m, n=m+1) {
console.log(m, n)
}
foo(10);
//------------------------
var bar = () => {
console.log(this, arguments)
}
console.log(bar.prototype) //undefined
const b = new bar() //报错:bar is not a constructor
箭头函数没有显式原型
箭头函数没有this 和 arguments,上面代码会去父级作用域查找 this 和 arguments
箭头函数不能new操作
展开语法(Spread syntax) :
· 可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开;
· 还可以在构造字面量对象时,将对象表达式按key-value的方式展开;
const names = ["abc", "cba", "nba"]
const name = "why"
//1.函数调用时
function foo(x, y, z) {
console.log(x,y,z)
}
// foo.apply(null, names)
foo(...names)
foo(...name)
//2.构造数组时
const newNames = [...names, ...name]
console.log(newNames)
//3.ES2018(ES9)
const info = {name: "why", age: 18}
const obj = {...info, address:"广州市"}
console.log(obj)
//4.构建对象字面量时ES2018(ES9)
const obj = { ...info, address: "广州市", ...names }
console.log(obj)
01:51:00
展开语法 实际做的是浅拷贝
const info = {
name: "why",
friend: { name: "kobe" }
}
const obj = { ...info, name: "coderwhy" } //展开后,又把 name属性值覆盖了
// console. log (obj)
obj.friend.name = "james"
console.log(info.friend.name) //james
【浅拷贝,这边改变 obj.friend对象的name属性值,info对象里的也相应改变了】
ES6中表示数值的方式:
const num1 =100 //十进制
const num2 =0b100 //二进制 binary
const num3 =0o100 //八进制 octonary
const num4 =0x100 // 十六进制 hexadecimal
console.log(num1, num2, num3, num4) //100 4 64 256
//大的数值的连接符(ES2021-ES12)
const num = 10_000_000_000_000_000
console.log(num) //10000000000000000
// 1.ES6之前,对象的属性名(key)
var obj = {
name: "why",
friend: { name: "kobe" },
age:18
}
obj['newName']= "james"
console.log(obj)
// 2.ES6中Symbol的基本使用
const s1 = Symbol()
const s2 = Symbol()
console.log(s1===s2) //false
// ES2019(ES10)中,Symbol还有一个描述(description)
const s3 = Symbol("aaa")
console.log(s3.description)
// 3.Symbol值作为key
// 3.1.在定义对象字面量时使用
const obj = {
[s1]:"abc",
[s2]:"cba"
}
// 3.2.新增属性
obj[s3] = "nba"
// 3.3.0bject.defineProperty方式
const s4 = Symbol()
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: "mba"
})
console.log(obj[s1], obj[s2], obj[s3], obj[s4]) //abc cba nba mba
//·注意:不能通过 . 语法获取
console.log(obj.s1) //undefined
// 4.使用Symbol作为key的属性名,在遍历/Object.keys等中是获取不到这些Symbol值
// 需要Object.getOwnPropertySymbols来获取所有Symbol的key
console.log(Object.keys(obj)) //[] 获取到空数组
console.log(Object.getOwnPropertyNames(obj)) //[]
console.log(Object.getOwnPropertySymbols(obj)) //这种可以获取到所有Symbols键数组
const sKeys = Object.getOwnPropertySymbols(obj)
for (const skey of sKeys) {
console.log(obj[sKey])
}
// 5.Symbol.for(key)/Symbol.keyFor(symbol) 创建相同的键
const sa = Symbol.for("aaa")
const sb = Symbol.for("aaa")
console.log(sa === sb) //true
const key =Symbol.keyFor(sa)
console.log(key)
const sc = Symbol.for(key)
console.log(sa === sc) //true
Symbol的基本使用
Symbol是什么呢?Symbol是ES6中新增的一个基本数据类型,翻译为符号。
那么为什么需要Symbol呢?
· 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突;
· 比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下,很容易造成冲突,从而覆盖掉它内部的某个属性;
· 比如我们前面在讲apply、call、bind实现时,我们有给其中添加一个fn属性,那么如果它内部原来已经有了fn属性了
呢?
· 比如开发中我们使用混入,那么混入中出现了同名的属性,必然有一个会被覆盖掉;
Symbol就是为了解决上面的问题,用来生成一个独一无二的值。
· Symbol值是通过Symbol函数来生成的,生成后可以作为属性名;
· 也就是在ES6中,对象的属性名可以使用字符串,也可以使用Symbol值;
Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的;
我们也可以在创建Symbol值的时候传入一个描述description:这个是ES2019(ES10)新增的特性;