一、ES6 和 JavaScript
ECMAScript、JavaScript、NodeJs,它们的区别是什么?
ECMAScript:简称ES,是一个语言标准(循环、判断、变量、数组等数据类型)
JavaScript:运行在浏览器端的语言,该语言使用ES标准。 ES + web api = JavaScript
NodeJs:运行在服务器端的语言,该语言使用ES标准。 ES + node api = JavaScript
无论JavaScript,还是NodeJs,它们都是ES的超集(super set)
ES6.0: 2015, 从该版本开始,不再使用数字作为编号,而使用年份
二、let和const命令
1、使用var声明变量
(1)允许重复的变量声明:导致数据被覆盖
var a = 100
//一万行代码
var a = 1
(2)变量提升:怪异的数据访问、闭包问题(只有函数级作用域,没有块级作用域)
//先声明一个var a,再执行if-else
if(Math.random()<0.5){
var a = "abc"
console.log(a)
}else{
console.log(a)
}
console.log(a)
(3)全局变量挂载到全局对象:全局对象成员污染问题
var a = 10
console.log(window.a)//10
2、使用let声明变量
块级作用域:代码执行时遇到花括号,会创建一个块级作用域,花括号结束,销毁块级作用域。
let
命令,用来声明变量。但是所声明的变量,只在let
命令所在的代码块内有效。
(1)let声明的变量不会挂载到全局对象
顶层对象
顶层对象,在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。
ES6 规定,为了保持兼容性,var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
console.log(window.a) // 1
let a = "ll"
console.log(window.a)//undefined
(2)不允许当前作用域范围内重复声明。在块级作用域中用let定义的变量,在作用域外不能访问
console.log(name)//会报错
let name = "ll"
console.log(name)//"ll"
let name = "lk" //报错,检查到当前作用域已经声明过,不允许重复声明
(3)使用let不会有变量提升,因此,不能在定义let变量之前使用它
// var 的情况
console.log(a); // 输出undefined
var a = 2;
// let 的情况
console.log(b); // 报错ReferenceError
let b = 2;
暂时性死区
var a = 10
if(true){
a = 5
let a = 10
}
存在全局变量a,但是if后的{}块级作用域内let又声明了一个局部变量a,导致后者绑定这个块级作用域,所以在let声明变量前,对a赋值会报错。
ES6 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
底层实现上,let声明的变量实际上也会有提升,但是,提升后会将其放入到“暂时性死区”,如果访问的变量位于暂时性死区,则会报错:“Cannot access ‘a’ before initialization”。当代码运行到该变量的声明语句时,会将其从暂时性死区中移除。
在循环中,用let声明的循环变量,会特殊处理,每次进入循环体,都会开启一个新的作用域,并且将循环变量绑定到该作用域(每次循环,使用的是一个全新的循环变量)。在循环中使用let声明的循环变量,在循环结束后会销毁
3、使用const声明常量
const
和let
完全相同,仅在于const
声明一个只读的常量。一旦声明,常量的值就不能改变。
实际上,在开发中,应该尽量使用const来声明变量,以保证变量的值不会随意篡改,原因如下:
1. 根据经验,开发中的很多变量,都是不会更改,也不应该更改的。
2. 后续的很多框架或者是第三方JS库,都要求数据不可变,使用常量可以一定程度上保证这一点。
(1)const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
const a = 1
a = 3;// TypeError: Assignment to constant variable.
const b;// SyntaxError: Missing initializer in const declaration
(2)常量不可变,是指声明的常量的内存空间不可变,并不保证内存空间中的地址指向的其他空间不可变。
const a = {};
// 为 a 添加一个属性,可以成功
a.prop = 123;
console.log(a.prop) // 123
// 将 a 指向另一个对象,就会报错
a = {}; // TypeError: "a" is read-only
(3)命名规范
1. 特殊的常量:该常量从字面意义上,一定是不可变的,比如圆周率、月地距地或其他一些绝不可能变化的配置。通常,该常量的名称全部使用大写,多个单词之间用下划线分割
2. 普通的常量:使用和之前一样的命名即可
(4)在for循环中,循环变量不可以使用常量,for-in循环可以
三、字符串的扩展
1、更好的Unicode支持
早期,由于存储空间宝贵,Unicode使用16位二进制来存储文字。我们将一个16位的二进制编码叫做一个码元(Code Unit)。后来,由于技术的发展,Unicode对文字编码进行了扩展,将某些文字扩展到了32位(占用两个码元),并且,将某个文字对应的二进制数字叫做码点(Code Point)。
一个字符对应一个码点(且每个字符的码点都是唯一的),但一个码点可能需要一个代码单元或者两个代码单元才能表示。
charCodeAt():读取一个码元
例子 𠮷:码点是0x20BB7
,UTF-16 编码为0xD842 0xDFB7
const text = "𠮷"; //占用了两个码元(32位)
console.log("字符串长度:", text.length); //2
console.log("使用正则测试:", /^.$/.test(text));//false 读到了两个码元,希望匹配一个
console.log("得到第一个码元:", text.charCodeAt(0));//55362
console.log("得到第二个码元:", text.charCodeAt(1));//57271
codePointAt():根据字符串码元的位置得到其码点。能够正确处理 4 个字节储存的字符,返回一个字符的码点。
//𠮷:\ud842\udfb7
console.log("得到第一个码点:", text.codePointAt(0));//134071 获取完整的
console.log("得到第二个码点:", text.codePointAt(1));//57271
同时,ES6为正则表达式添加了一个flag: u,如果添加了该配置,则匹配时,使用码点匹配
console.log("使用正则测试:", /^.$/u.test(text));
/**
* 判断字符串char,是32位,还是16位
* @param {*} char
*/
function is32bit(char, i) {
//如果码点大于了16位二进制的最大值,则其是32位的
return char.codePointAt(i) > 0xffff;
}
/**
* 得到一个字符串码点的真实长度
* @param {*} str
*/
function getLengthOfCodePoint(str) {
var len = 0;
for (let i = 0; i < str.length; i++) {
//i在索引码元
if (is32bit(str, i)) {
//当前字符串,在i这个位置,占用了两个码元
i++;
}
len++;
}
return len;
}
console.log("𠮷 是否是32位的:", is32bit("𠮷", 0))
console.log("ab𠮷ab 的码点长度:", getLengthOfCodePoint("ab𠮷ab"))
字符串的遍历器for...of
循环遍历
const text = "𠮷as";
for(let i=0;i<text.length;i++)
{
console.log(text[i]) //� � a s
}
//普通for循环无法正确识别。一次只能读取一个代码单元
//for-of可以正确识别一个完整的符号
const text = "𠮷as";
for(const item of text)
{
console.log(item) //𠮷 a s
}
2、字符串新增API
以下均为字符串的实例方法
(1)includes():判断字符串中是否包含指定的子字符串,第二个参数指定从哪个位置开始查找,返回true和false
const str = "今天天气很好啊!"
const result1 = str.includes("气")
const result2 = str.includes("气",5)
console.log(result1,result2)
(2)startsWith():判断字符串中是否以指定的字符串开始,也可以传第二个参数
const str = "今天天气很好啊!"
const result = str.startsWith("气")
console.log(result)
(3)endsWith():判断字符串中是否以指定的字符串结尾
const str = "今天天气很好啊!"
const result = str.endsWith("!")
console.log(result)
(4)repeat():将字符串重复指定的次数,然后返回一个新字符串。
const str = "我很帅!"
const result = str.repeat(4)
console.log(result)//我很帅!我很帅!我很帅!我很帅!
//如果repeat的参数是负数或者Infinity,会报错。
const result = 'a'.repeat(Infinity)// RangeError
const result = 'a'.repeat(-1)// RangeError
补充:
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。实例方法
(5)padStart():第一个参数是字符串补全生效的最大长度,第二个参数是用来从起始位置补全的字符串。
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(4) // 'x '
(6)padEnd() :第一个参数是字符串补全生效的最大长度,第二个参数是用来从结束位置补全的字符串。
'x'.padEnd(5, 'ab') // 'xabab'
'12'.padStart(10, '0') // "0000000012"
注意:省略第二个参数,则默认用空格补全。padEnd方法常用来数字前补零
ES2019对字符串实例新增了trimStart()
和trimEnd()
这两个方法。它们的行为与trim()
一致,trimStart()
消除字符串头部的空格,trimEnd()
消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
(7)trimStart()
和trimEnd()
:消除头部/尾部空格
const str = ' abc ';
consst r1 = str.trim() // "abc"
consst r2 = str.trimStart() // "abc "
consst r3 = str.trimEnd() // " abc"
注意:除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。浏览器还部署了额外的两个方法,trimLeft()
是trimStart()
的别名,trimRight()
是trimEnd()
的别名。
3-3. [扩展]正则中的粘连标记 几乎用不到
标记名:y
含义:匹配时,完全按照正则对象中的lastIndex位置开始匹配,并且匹配的位置必须在lastIndex位置。
const text = "Hello World!!!"
const reg = /w\w+/y;//以w开头的单词
reg.lastIndex = 6;
//从lastIndex的位置必须匹配,一开始lastIndex为0
console.log(reg.test(text))
3、模板字符串
模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
const text =
`我很帅
我真的很帅 `;
console.log(text);
如果要在字符串中拼接js表达式,只需要在模板字符串中使用${JS表达式}
const age = 18
console.log(`我今年${age}岁`)
console.log(`计算结果:${1+2+3*2}`)
4、模板字符串标记
在模板字符串书写之前,可以加上标记:
标记名`模板字符串`
标记是一个函数,函数参数如下:
- 参数1:被插值分割的字符串数组
- 后续参数:所有的插值
const name = "llx"
const age = 18
// 相当于 text = myTag(["我的名字叫:",",我今年","岁"],"llx",18)
var text = myTag`我的名字叫:${name},我今年${age}岁`
function myTag(parts,arg1,arg2){
console.log(parts)
console.log(arg1)
console.log(arg2)
}
console.log(text)//(3) ["我的名字叫:", ",我今年", "岁", raw: Array(3)]
const name = "llx"
const age = 18
var text = myTag`我的名字叫:${name},我今年${age}岁`
// 相当于
// text = myTag(["我的名字叫:",",我今年","岁"],"llx",18)
function myTag(parts){
console.log(parts)
//先将arguments转化为真实数组,再除去第一个参数,parts
const values = Array.prototype.slice.apply(arguments).splice(1)
console.log(values)
let str = ""
for(let i=0;i<parts.length;i++){
if(parts.length-1 == i){
str += parts[i]
}else{
str += parts[i]+values[i]
}
}
return str
}
console.log(text)
String.raw将转义字符原样输出
const text = String.raw`abc\ndef`
console.log(text)
四、函数的扩展
1、 参数默认值
在书写形参时,直接给形参赋值,附的值即为默认值。这样一来,当调用函数时,如果没有给对应的参数赋值(给它的值是undefined),则会自动使用默认值。
//c的默认值是2
function sum(a = 0,b = 0,c = 2){
return a + b + c;
}
console.log(sum(1,undefined,3))
注意:参数变量是默认声明的,所以不能用let
或const
再次声明。
对arguments的影响
只要给函数加上参数默认值,该函数会自动变量严格模式下的规则:arguments和形参脱离
比较二者区别
function test(a,b){
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
a = 3
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
}
test(1,2)
function test(a,b=1){
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
a = 3
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
}
test(1,2)
暂时性死区
形参和ES6中的let或const声明一样,具有作用域,并且根据参数的声明顺序,存在暂时性死区。
function test(a,b = a){
console.log(a,b)
}
test(1,2)
test(undefined,1)
function test1(a = b,b){
console.log(a,b)
}
test1(1,2)
test1(undefined,1)//报错 Cannot access 'b' before initialization
2、rest参数
ES6的rest参数用于获取函数的多余参数,这样就不需要使用arguments
对象了。rest 参数会将多余的参数放入数组中。形如:...形参名
arguments
对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call
先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
function test(a,...args){
console.log(a,args)//1 [2, 3, 4, 5]
}
test(1,2,3,4,5)
function sum(...args){
let result = 0;
for(let i=0;i<args.length;i++){
result += args[i];
}
return result
}
console.log(sum(1,2,3,4,5))
注意:一个函数,仅能出现一个剩余参数。一个函数,如果有剩余参数,剩余参数必须是最后一个参数
补充:从 ES5 开始,函数内部可以设定为严格模式。ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
3、展开运算符
使用方式:...要展开的东西
(1)对数组展开 ES6
//克隆数组arr1到arr2
const arr1 = [1,2,3,4,5]
const arr2 = [...arr1]
例子:
function sum(...args){
let result = 0;
for(let i=0;i<args.length;i++){
result += args[i];
}
return result
}
function getRandomNumbers(length){
let arr = []
for(let i=0;i<length;i++){
arr.push(Math.random())
}
return arr
}
const numbers = getRandomNumbers(10)
//展开的内容是numbers数组中的,相当于传递了10个参数
const result = sum(...numbers)
console.log(result)
(2)对对象展开 (ES2017)
const obj1 = {
name:"lkx",
age:18,
address:{
country:"山东"
}
}
//浅克隆
const obj2 = {
...obj1,
age:10
}
//克隆深度对象
const obj3 = {
...obj,
age:10,
address:{
...obj1.address
}
}
柯里化:用户固定某个函数的前面的参数,得到一个新的函数,新的函数调用时,接收剩余的参数
function cal(a,b,c,d){
return a + b *c -d
}
// 柯里化函数
function curry(func,...args){
return function(...subArgs){
const allArgs = [...args,...subArgs]
if(allArgs.length>=func.length){
//参数够了
return func(...allArgs)
}else{
//参数不够,继续固定
return curry(func,...allArgs)
}
}
}
const newCal = curry(cal,1,2)
console.log(newCal(2,4))
4、 明确函数的双重用途
ES6提供了一个特殊的API,可以使用该API在函数内部,判断该函数是否使用了new来调用
new.target
//该表达式,得到的是:如果没有使用new来调用函数,则返回undefined
//如果使用new调用函数,则得到的是new关键字后面的函数本身
function Person(firstName,lastName){
//判断是否是使用new的方式来调用的函数
//过去的判断方式,判断this指向,但是用call和apply可能绕开
if(this instanceof Person){
throw new Error("该函数没有使用new创建")
}
//es6提供的判断方式
if(new.target === undefined){
throw new Error("该函数没有使用new创建")
}
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + lastName
}
const p1 = new Person("张","飞")
console.log(p1)
const p2 = Person("张","飞")
console.log(p2)
5、箭头函数
ES6 允许使用“箭头”(=>
)定义函数。形如:(参数1, 参数2, ...)=>{ //函数体 }
var func = value => value;
// 等同于
var func = function (value) {
return value;
};
箭头函数是一个函数表达式,理论上,任何使用函数表达式的场景都可以使用箭头函数
const printNums = (num1,num2,num3) =>{
const result = num1 + num2 * num3
console.log(result)
}
如果参数只有一个,可以省略小括号
const print = num =>{
num += 1
console.log(num)
}
如果箭头函数只有一条返回语句,可以省略大括号,和return关键字
//返回true和false
const isOdd = num => num%2 !== 0
注意:由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getObject = id => { id: id, name: "ll" };
// 不报错
let getObject = id => ({ id: id, name: "ll" });
关于this指向
在以往的学习中,对于this指向
1. 通过对象调用函数,this指向对象
2. 直接调用函数,this指向全局对象
3. 如果通过new调用函数,this指向新创建的对象
4. 如果通过apply、call、bind调用函数,this指向指定的数据
5. 如果是DOM事件函数,this指向事件源
//想要在定时器,事件中使用this,需要提前保存起来
const obj = {
count : 0,
start:function(){
let _this = this
setInterval(function(){
_this.count++
console.log(_this.count)
}, 1000);
},
regEvent:function(){
let _this = this
window.onclick = function(){
console.log(_this.count)
}
}
}
obj.start()
obj.regEvent()
而箭头函数中的this指向,他是固定的。函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。
箭头函数有几个使用注意点。
(1)函数体内的`this`对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用`new`命令,否则会抛出一个错误。
(3)不可以使用`arguments`对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用`yield`命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。`this`对象的指向是可变的,但是在箭头函数中,它是固定的。
箭头函数中,不存在this、arguments、new.target,如果使用了,则使用的是函数外层的对应的this、arguments、new.target
//箭头函数中的this取决于声明箭头函数的位置
const obj = {
count: 0,
start:function(){
// let _this = this
setInterval(() => {
this.count++
console.log(this.count)
}, 1000);
},
regEvent:function(){
window.onclick = ()=>{
console.log(this.count)
}
}
}
obj.start()
obj.regEvent()
const func = ()=>{
console.log(this)//window
console.log(arguments)//Uncaught ReferenceError
}
const obj = {
method:func
}
obj.method(234)
const obj = {
method:function(){
const func = ()=>{
console.log(this)//obj
console.log(arguments)//method的arguments
}
func()
}
}
obj.method(234)
应用场景
- 临时性使用的函数,并不会可以调用它,比如:
- 事件处理函数
- 异步处理函数,settimeout,setinterval
- 其他临时性的函数
- 为了绑定外层this的函数
- 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
const numbers = [3,5,6,32,78,24]
const result = numbers.filter(num=>num%2!==0).map(num=>num*2).reduce((a,b)=>a+b)
console.log(result)
6、Function.prototype.toString()
ES2019 对函数实例的**toString()**方法做出了修改。
toString()
方法返回函数代码本身,以前会省略注释和空格。
7、catch 命令的参数省略
JavaScript 语言的try...catch
结构,以前明确要求catch
命令后面必须跟参数,接受try
代码块抛出的错误对象。ES2019对try...catch
,允许catch语句省略参数。
try {
// ...
} catch (err) {
// 处理错误
}
//---Es2019以后
try {
// ...
} catch {
// ...
}
五、对象的扩展
1、新增的对象字面量语法
(1)成员速写
如果对象字面量初始化时,成员的名称来自于一个变量,并且和变量的名称相同,则可以进行简写
const name = "张飞"
const age = 18
const obj = {
name,
age
}
(2)方法速写
对象字面初始化时,方法可以省略冒号和function关键字
const obj = {
sayHellow(){
console.log("hello world")
}
}
(3)计算属性名
有的时候,初始化对象时,某些属性名可能来自于某个表达式的值,在ES6,可以使用中括号来表示该属性名是通过计算得到的。
const key = "name"
const name = "lkx"
const obj = {
[key]:name
}
(4)对象属性的遍历
ES6 一共有 5 种方法可以遍历对象的属性。(在Es6后续中介绍)
(1)for…in
for...in
循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
(2)Object.keys(obj)
Object.keys
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有 Symbol 属性的键名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys
返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串键,按照加入时间升序排列。
- 最后遍历所有 Symbol 键,按照加入时间升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
上面代码中,Reflect.ownKeys
方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2
和10
,其次是字符串属性b
和a
,最后是 Symbol 属性。
2、链判断运算符和Null 判断运算符
(1)链判断运算符
链判断运算符有三种用法:
1、obj?.prop // 对象属性
2、obj?.[expr] // 同上
3、func?.(...args) // 函数或对象方法的调用
在项目中经常会读取某个对象中的属性。例如:result.message.user.firstName
我们通常的写法为。
const firstName = (result && result.message && result.message.user&& result.message.user.firstName) || '暂无数据';
这样的层层判断非常麻烦,因此ES2020引入了链判断运算符: ?.
,简化上面的写法。
const firstName = result?.message?.user?.firstName || '暂无数据';
使用了?.
运算符,直接在链式调用的时候判断,左侧的对象是否为null
或undefined
。如果是的,就不再往下运算,而是返回undefined
。
(2)Null判断运算符
我们在读取对象属性的时候,如果某个属性的值是null
或undefined
,有时候需要为它们指定默认值。我们经常用||
运算符指定默认值。
const firstName = result.firstName || '暂无数据';
但是属性的值如果为空字符串或false
或0
,默认值也会生效。为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??
。它的行为类似||
,但是只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。
const firstName = result.firstName ?? '暂无数据';
该运算符一般配合.?
使用
const firstName = result?.message?.user?.firstName ?? '暂无数据';
3、Object的新增API
(1)Object.is()
用于判断两个数据是否相等,基本上跟严格相等(===)是一致的,除了以下两点:
- NaN和NaN相等
- +0和-0不相等
console.log(NaN === NaN) //false
console.log(+0 === -0) //true
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
(2)Object.assign(target,source1,source2…)
方法的第一个参数是目标对象,后面的参数都是源对象。将源对象(source)的所有可枚举属性,复制到目标对象(target)。
const obj1 = {
name:"l"
}
const obj2 = {
name:"lkx",
age:18
}
const obj = Object.assign(obj1,obj2)
console.log(obj)
console.log(obj1 === obj) //true
注意:用于混合对象,后面的覆盖前面的,会改变obj1
//通常我们会把第一个参数写为一个空对象
const lastObj = Object.assign({},obj1,obj2)
再注意:方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。一旦遇到同名属性,Object.assign
的处理方法是替换,而不是添加。
(3)Object.getOwnPropertyNames
Object.getOwnPropertyNames方法之前就存在,只不过,官方没有明确要求,对属性的顺序如何排序,如何排序,完全由浏览器厂商决定。
ES6规定了该方法返回的数组的排序方式如下:
- 先排数字,并按照升序排序
- 再排其他,按照书写顺序排序
const obj = {
a:"a",
b:"b",
c:"c",
0:0,
1:1
}
console.log(Object.getOwnPropertyNames(obj))
//["0", "1", "a", "b", "c"]
(4)Object.setPrototypeOf()和Object.getPrototypeOf()
setPrototypeOf() 该函数用于设置某个对象的隐式原型__proto__
比如: Object.setPrototypeOf(obj1, obj2),
相当于: obj1.__proto__ = obj2
const obj1 = {
a:1
}
const obj2 = {
b:2
}
Object.setPrototypeOf(obj1,obj2)
//将obj1的隐式原型改为obj2
Object.getPrototypeOf():该方法与Object.setPrototypeOf
方法配套,用于读取一个对象的原型对象。
(5)Object.keys(),Object.values(),Object.entries() ,Object.fromEntries()
Object.keys():返回一个数组,返回对象的所有属性的键名。(ES5 )
var obj = { a: 1, b: 2 };
Object.keys(obj) // ["a", "b"]
Object.values():方法返回一个数组,返回对象的所有属性的键值。(ES2017)
var obj = { a: 1, b: 2 };
Object.values(obj) // ["1", "2"]
Object.entries():方法返回一个数组,返回对象的所有属性键值对数组。(ES2017)
var obj = { a: 1, b: 2 };
Object.entries(obj) // [["a","1"], ["b","2"]]
Object.fromEntries() :是Object.entries()
的逆操作,用于将一个键值对数组转为对象。
var arr = [["a","1"], ["b","2"]]
Object.fromEntries(arr)//{ a: 1, b: 2 }
六、变量的解构赋值
解构:使用ES6的一种语法规则,将一个对象或数组的某个属性提取到某个变量中。解构不会对被解构的目标造成任何影响。
1、对象解构
const user = {
name:"lkx",
age:18,
address:{
provice:"山东",
city:"济南"
}
}
const {name,age,sex,address} = user
console.log(name,age,address) //lkx 18 {provice: "山东", city: "济南"}
在解构中使用默认值
//{同名变量 = 默认值}
const {name,age,address,sex="男"} = user
console.log(name,age,address,sex)//lkx 18 {provice: "山东", city: "济南"} 男
2、非同名属性解构
{属性名:变量名}
const {name,age:nianling=20,address,sex="男"} = user
console.log(name,nianling,address,sex)
3、数组解构
const numbers = ["a","b","c","d"]
const [n0,n1] = numbers
console.log(n0,n1)
const {2:n2,3:n3} = numbers
console.log(n2,n3)
4、参数解构
function print({ name, age, sex, address: {province,city} }) {
console.log(`姓名:${name}`)
console.log(`年龄:${age}`)
console.log(`性别:${sex}`)
console.log(`省份:${province}`)
console.log(`城市:${city}`)
}
const user = {
name: "lkx",
age: 18,
sex: "男",
address: {
province: "山东",
city: "济南"
}
}
print(user)