es6 —— 数组和对象的扩展

一、 数组的扩展

1. 扩展运算符
  1. 主要用于函数调用。当用扩展运算符给push方法传参时可实现分步push。具体见例1
  2. 如果扩展运算符后面是一个空数组则不会有什么效果
  3. 扩展运算符后面可以放置表达式
  4. 扩展运算符只有放在函数调用的圆括号里才不会报错
  5. 在es5中会用apply方法去将数组分解成一个个元素赋值给函数的形参,在es6中可直接使用扩展运算符
  6. ES5通过Array.prototype.push.apply(arr1,arr2)将arr2数组添加到arr1数组上,而ES6可直接通过扩展运算符添加
let [a,...b] = [1,2,3,4];
a//1
b//[2,3,4]
let a = [1,2,3,4,4];
console.log(...a);//12344
//例1
let a = [1,2,3];
a.push([23,4]);//返回结果为数组长度,4,a为[1,2,3,[23,4]]
a.push(...[23,4]);//数组长度为5,a为[1,2,3,23,4]
//放置表达式
const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];
//空数组
[...[],1]//[1]
console.log((...[1,2]));//Uncaught SyntaxError: Unexpected number
console.log(...[1,2]);//1,2 扩展运算符放在函数调用的圆括号里,故不报错
//针对5的练习
//es5写法
function f(x,y,z){
	console.log(x,y,z)
}
var args = [1,2,3];
f.apply(null,args);1,2,3

//es6写法
f(...args);//1,2,3
//针对6的练习
//es5
var arr1 = [1,2,3];
var arr2 = [2,3,4];
Array.prototype.push.apply(arr1,arr2);//[1,2,3,2,3,4]

//es6
arr1.push(...arr2);//[1,2,3,2,3,4]
扩展运算符的应用
① 复制数组
  1. 在es6之前,直接将数组的名字赋值给一个新的数组只会赋值原数组的指针,并不是真正的拷贝,故需要通过旧数组.concat()赋值给新数组才可得到真正的拷贝数组。
  2. 在es6中,可直接通过扩展运算符的方法实现数组的拷贝
//es5
const a1=[1,2];
const a2 = a1;
a2[0]=2;
//a1:[2,2];

//es5正确克隆
const a1 = [1,2];
const a2 = a1.concat();
a2[0] = 3;
//a1:[1,2]

//es6
const a1 = [1,2];
const a2 = [...a1];//[1,2]
② 合并数组
  1. es5合并数组需要用到concat()方法,es6只需将不同数组写成[…arr1,…arr2,…arr3]的形式即可合并数组
  2. 注意:1中两个方法实现的都是浅拷贝,如果修改了原数组或者新数组的成员,会同步反应到新数组(注意:这里修改的不是引用,而是值。若修改了引用,只会改变自身,不会改变其他数组)
//es5
const arr1 = [1,2];
const arr2 = [3,4];
const arr3 = [5,6];
arr1.concat(arr2,arr3);//[1,2,3,4,5,6]

//es6
[...arr1,...arr2,...arr3];//[1,2,3,4,5,6]
③ 可以与解构赋值结合

注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错

const [first,...rest] = [1,2,3,4];
first;//1
rest//[2,3,4]

const [first,...rest]= [];
first;//undefined
rest//[]

const [first,...rest] = ['foo'];
first//"foo"
rest//[]
④ 扩展运算符可以将字符串转成真正的数组
[...'hello'];//["h","e","l","l","o"]
⑤ 任何定义了遍历器(Iterator)接口的对象都可以用扩展运算符转为真正的数组。
//nodeList 定义了遍历器
let nodeList = document.querySelector("div");
let array = [...nodeList];//array为对象数组,nodeList不是真正的数组
//obj未定义遍历器
 let obj = {
            0:1,
            1:2,
            2:3,
            length:3
        }
        console.log([...obj]);
2. 数组新增方法
① 将类数组转换成数组:Array.from(类数组)

类数组的特征:有length属性,每个元素有其对应的索引

let obj = {
	0:1,
	1:2,
	2:3,
	length:3
}
Array.from(obj);//[1,2,3]
//非类数组
let obj = {
	a:1,
	b:2,
	c:3,
	length:3
}
Array.from(obj);//[undefined,undefined,undefined]
let obj = {
	a:1,
	b:2,
	c:3
}
Array.from(obj);//[]
② 将值转化成数组:Array.of(temp1,temp2,temp3…)
console.log(Array.of(1,2,3,4));//[1,2,3,4]
③ 遍历数组返回第一个符合回调函数中条件的值:对象.find(fn)
  1. 若没有找到满足条件的元素则返回undefined
  2. 当找到符合条件的值后不再判断数组中的其他元素
  3. find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
  4. 该方法可接受第二个参数,用来绑定回调函数的this对象
  5. 该方法可以发现NaN,弥补了indexof方法的不足
let a = [1,2,3,4];
console.log(a.find(()=>a>2));//返回3,找到第一个大于2的值并返回
 [NaN].indexOf(NaN);//-1
console.log([NaN].find(function (n) {
      return Object.is(NaN, n);
}));//NaN
④ 遍历数组返回第一个符合回调函数中条件的值的索引:对象.findIndex(fn)
  1. 当找到符合条件的值后不再判断数组中的其他元素
  2. 当没有找到满足条件的元素则返回-1
  3. 该方法可接受第二个参数,用来绑定回调函数的this对象
  let obj = {
            a:1,
            b:2,
            v:3
        }
  console.log(a.findIndex(function (n){
            console.log(this);//this为Obj
            if(n>2){
                return n;
            }
        },obj))
 [NaN].indexOf(NaN);//-1
        console.log([NaN].findIndex(function (n) {
            return Object.is(NaN, n);
        }));//0
⑤ 用参数值填充数组:数组对象.fill(待填充值)
  1. 若只有一个参数,该参数将数组中所有元素都替换
  2. 若该方法有三个参数,则参数依次为:被替换的值,起始位置索引,终止位置索引。(替换的最后一个索引为终止位置索引-1
  3. 注意:如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,故当任意一个元素中的值改变时,其余元素的值都改变
let a = [1,2,3,4];
a.fill(7);//a=[7,7,7]
let a = [1,2,3,5,6,7];
        a.fill("a",0,4);
        console.log(a);//["a","a","a","a",6,7]
let arr = new Array(3).fill({name:"Mike"});
arr[0].name = "Ben";
arr;//[{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]

对象的扩展

1. 对象属性和方法的简写
//属性的简写
let foo = "你好";
let obj = {
	foo,//相当于foo:"你好"
	hello:function(){
	}
}

const foo="bar";
const baz = foo;
baz;//{foo:bar}
//等同于
const baz={foo:"bar"}
function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}
//方法的简写
const o ={
	method(){
		return "hello";
	}
	//相当于
	method:functuon(){
		return "hello"
}
}
o.method();//hello
//对象的属性和方法简写
let birth = '2000/01/01';

const Person = {

  name: '张三',

  //等同于birth: birth
  birth,

  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }

};
2. 属性的取值器getter和赋值器setter(有时间可深度研究)
  1. 属性的赋值器(setter)和取值器(getter)也是采用属性和方法简写的方式
  2. 当给属性赋值或者获取属性值时会调用与该属性名称相同的方法,但是在方法中不能设置与属性名相同的属性,否则会持续执行get/set方法。但这种方法会给对象添加一个新的属性,赋值器和取值器方法名称中的属性不会显示出现在最终对象中。(具体看下例)
  3. 系统的取值器和赋值器请参考js源码
//最终cart对象不会显示出现wheels属性
const cart = {
  _wheels: 4,

  get wheels () {
    return this._wheels;
  },

  set wheels (value) {
    if (value < this._wheels) {
      throw new Error('数值太小了!');
    }
    this._wheels = value;
  }
}
cart.wheels = 5;//会调用set wheels方法,如果set后面的名称和属性名不相同,则不会调用
console.log(cart.wheels);//会调用get wheels方法,如果get后面的名称和属性名不相同,则不会调用

wheels对象并不显示展示:
在这里插入图片描述
在这里插入图片描述

//另一种设置赋值器和取值器的方法
let obj = {
             _num: 0,
             }

             Object.defineProperty(obj, "num", {
             get: function () {
             //返回值
             console.log("我在返回值");
             return this._num;
             },
             set: function (val) {
             console.log("设置值");
             try {
             if (val) {
             this._num = val;
             }
             else {
             throw  new Error("val is no value");
             }
             }
             catch (err) {
             console.log(err);
             }
             }
             })

             obj.num=2;//打印:设置值
             console.log(obj.num);//打印:我在返回值
            obj.age = 19;
            console.log(obj);//{_num:2,age:19}
3. super关键字
  1. super指向当前对象的原型对象(可以理解为后端中的父类)
  2. super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。目前,只有对象方法的简写法可以让 JavaScript 引擎确认,定义的是对象的方法。故只能在对象方法的简写中用super
  3. Object.setPrototypeOf(son,father)方法代表:设置father对象是son对象的原型对象,son对象可以调用father对象的方法以及获取到其的属性。
  4. 若用super调用的方法中用到this,该this依旧指向当前对象,而不是原型对象。
  5. JavaScript 引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)Object.getPrototypeOf(this).foo.call(this)(方法)
 const father = {
            a:1,
            b:4,
            getA(){
             console.log(this.a);
            }
        }
        const son = {
            a:2,
            getA(){
                super.getA();
            }
        }
        Object.setPrototypeOf(son,father);
        son.getA();//输出2
        console.log(son.b);//4
          console.log(son);

输出的son对象:
在这里插入图片描述

//错误的super使用
// 报错
const obj = {
//将得到的值赋给了foo属性
  foo: super.foo
}

// 报错
const obj = {
//此处的foo不被认作对象方法,它被认作对象的属性
  foo: () => super.foo
}

// 报错
const obj = {
//此处的foo不被认作对象方法,它被认作对象的属性
  foo: function () {
    return super.foo
  }
}
4. 对象扩展符
① 解构赋值
  1. 对象的解构赋值是从一个对象根据键值取值,并将尚未读取的属性分配到指定的对象上面,所有的键和它们的值都会被拷贝到新对象上(对象的解构赋值不要求左右对象的键顺序一样)
  2. 由于解构赋值要求右边必须是一个对象,故如果等号右边是null或undefined,会报错,因为它们无法转成对象
  3. 解构赋值必须是最后一个参数,否则会报错。
  4. 解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。当修改原对象的某一个键对应的值时,新对象中键对应的值也会改变。同理,修改新对象,原对象也会改变
  5. 扩展运算符的解构赋值不能复制继承自原型对象的属性。但单纯的解构赋值可以取到继承的属性
let  {c,...b} = {a:1,b:2,c:4};
c//4
b://{a:1,b:2}
let obj = {a:{b:1}};
let {...x} = obj;
obj.a.b = 2;
x.a.b//2
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }
o3.a // undefined
//注意点5的易错
const o = Object.create({ x: 1, y: 2 });//o的原型对象为{x:1,y:2}
o.z = 3;

let { x, ...newObj } = o;//x为单纯的解构赋值,故可以取到,而y是扩展运算符的解构赋值,故不能取到y,而z为o对象本身的属性,故可以取到
let { y, z } = newObj;
x // 1
y // undefined
z // 3
发布了72 篇原创文章 · 获赞 72 · 访问量 6320

猜你喜欢

转载自blog.csdn.net/weixin_43314846/article/details/102732680