ES6

103 阅读11分钟

对象的拓展

  • 写法太灵活了
const s = {
    demo: function(){
        
    }
}
等同于
const s = {
    demo(){
        
    }
}

方法的name属性

  • 对象的方法 也为函数,具有name属性
  • 如果对象内部存在set get 那么其对象的name属性,是附着在其set get上的 获得该属性的描述对象
  • enumerable 可枚举属性
  • for in 自身的以及继承的可枚举属性
  • Object.assign() 拷贝对象自身的可枚举属性
  • Object.keys() 自身的可枚举属性键名
  • JSON.stringily 串行化自身的可枚举属性

属性遍历的五种方法

  • for in 自身的和继承的所有属性(可枚举的)
  • Object.keys() 自身的所有属性(可枚举的)
  • Reflect.ownKeys() 自身的所有属性
  • Object.getOwnPropertyNames(obj) 自身的所有属性(非Symbol)
  • Object.getOwnPropertySymbols(obj) 自身的Symbol属性的键名

遍历规则

  • 数值 字符 Symbol

super 关键字

this总是指向函数所在的当前对象,super总是指向当前对象的原型对象

  • super目前只能在对象的方法中才能生效
const obj = {
    foo(){
        super.foo
    }
}

解构赋值

从一个对象取值。将可遍历的属性但未分配的属性,放置到指定的对象上。键和值都会拷贝在新对象上。

  • ... 拓展运算符 结果为对象
  • ... 等同于Object.assign
let obj = {
    ...a
}
===
const obj=Object.assign({},a)

上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。

方法一:
const objclone = {
    __proto__: Object.getPrototypeOf(obj),
    ...obj
}
方法二:
const objclone = Object.assign(
    Object.create(Object.getPrototypeOf(obj)),
    obj
)
方法二函数式写法:
function clone(obj){
    let demo = Object.getPrototypeOf(obj)
    return Object.assign(demo,obj)
}
方法三:
const clone3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

下方为拓展阅读,说明一下JS的内置对象的使用方法 Object.getOwnPropertyDescriptors Object.getPrototypeOf Object.getOwnPropertyDescriptors 与 Object.defineProperties() 方法合用,可以实现 assign 不能实现的对get set的拷贝

const source = {
  set foo(value) {
    console.log(value);
  }
};

const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
//   set: [Function: set foo],
//   enumerable: true,
//   configurable: true }

函数式写法
const shallowMerge = (target, source) => Object.defineProperties(
  target,
  Object.getOwnPropertyDescriptors(source)
);
手写
function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}
const object1 = {
  property1: 42,
  get foo() {},
  set foo(x) {}
};
const descriptor1 = Object.getOwnPropertyDescriptors(object1, 'object1');
console.log(descriptor1)

Object { property1: Object { value: 42, writable: true, enumerable: true, configurable: true }, foo: Object { get: get foo() {}, set: set foo(x) {}, enumerable: true, configurable: true } }

console.log(Object.getPrototypeOf(object1))
const object1 = {
  property1: 42,
  get foo() {},
  set foo(x) {}
};
console.log(Object.getPrototypeOf(object1))

Object {  }  // 非浏览器环境下的结果

浏览器结果
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

下方依然为拓展阅读 prototype proto

const object1 = {
  property1: 42,
  get foo() {},
  set foo(x) {}
};
console.log(Object.getPrototypeOf(object1))
console.log(object1.__proto__.Prototype)
console.log(object1.Prototype)
console.log(object1.__proto__)

结果为: Object.getPrototypeOf(object1) === object1.__proto__
我一开始认为是 Object.getPrototypeOf(object1) === object1.prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
undefined
undefined
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

prototype是构造器/函数才具有的属性 JavaScript内置的构造器有以下几个: Object,Number,Boolean,String,Array,RegExp,Date,Function,Error,Math,JSON等, 其中Math和JSON是以对象形式存在的,无需new便可创建。当我们用 var mm=new Math(1234565);的时候,会报错。 Object,Number,Boolean,String,Array,RegExp,Date,Function,Error的原型是Function.prototype。而Math和JSON对象的原型是Object.prototype。

对象的新增方法

  • Object.is(a,b) 判断两个值是否相等

==(相等运算符) ===(严格相等运算符)前者会自动转换数据类型,后者的NaN不等于自身 +0 和 -0相等

  • Object.assign(target,source1,source2) 是将目标对象的可枚举属性复制到源对象上
  • 只有字符串的包装对象,才会产生可枚举属性 注意点
  • 浅拷贝
  • 同名属性的替换
  • 数组的处理 将数组当成对象来处理
  • 取值函数的处理 只能进行值的复制,如果为取值函数。那么取值后进行处理

proto 属性

  • 其本质上不是一个对外暴漏的API
  • 目前推荐使用Object.setPrototypeOf(),Object.getPrototypeOf(),Object.create()
  • proto 调用的是Object.prorotype.proto
  • Object.setPrototypeOf(obj,proto)
function Rectangle() {
  // ...
}

const rec = new Rectangle();

Object.getPrototypeOf(rec) === Rectangle.prototype
// true
源码实现
Object.defineProperty(Object.prototype, '__proto__', {
  get() {
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto) {
    if (this === undefined || this === null) {
      throw new TypeError();
    }
    if (!isObject(this)) {
      return undefined;
    }
    if (!isObject(proto)) {
      return undefined;
    }
    let status = Reflect.setPrototypeOf(this, proto);
    if (!status) {
      throw new TypeError();
    }
  },
});

function isObject(value) {
  return Object(value) === value;
}

JS中创建对象的N种方式

  1. 创建一个Object的对象实例而后为其添加属性和方法
var person = new Object();
person.name = "zhang"
person.sayname = function(){
    alert(this.name)
}
  1. 对象字面量
var person = {
    name: "zhang"
    sayname: function(){
        
    }
}
  1. 工厂函数
function createperson(name,age,job){
    var o = new Object();
    o.name = name
    o.sayname = function(){
        
    }
    return o
}
var person1 = createperson("zhang",29,"engineer")
console.log(person1 instanceof createperson) false
console.log(person1 instanceof Object)  true
  1. 构造函数
function Person(name,age,job){
    this.name = name
    this.sayname = function(){
        
    }
    等同于
    this.sayname = new Function()
}
var person1 = new Person("zhang",29,"engineer")
console.log(person1 instanceof Person)  true
console.log(person1 instanceof Object)  true

new,所经历的四个过程

  • 创建一个新的对象
  • 将构造函数的作用域赋给新对象
  • 执行构造函数代码
  • 返回新对象

构造函数模式中person1.constructor === person

  1. 原型创建对象模式
function Person(){
    
}
Person.prototype.name = 'Nike'
Person.prototype.sayName = function(){
    
}
var person1 = new Person();

6.组合使用构造函数模式和原型模式

function Person(name,age,job){
 this.name =name;
 this.age = age;
 this.job = job;
}
Person.prototype = {
 constructor:Person,
 sayName: function(){
 alert(this.name);
 };
}
var person1 = new Person('Nike',20,'teacher');

xdm,学费了

运算符的拓展

没啥可以记的

Symbol

感觉也没啥可以记的

Set 和 Map 数据结构

  • Set 集合
  • Map Hash表 与对象类似
  • 不同的是 Object 结构提供了 字符串-值得对应 Map结构提供了值-值得对应

具体有需要再看吧,不是应用得重点

Proxy 拦截

  • 所有从外部的访问,首先需要通过这层拦截。从而提供了一种机制,可以对外界的访问过滤以及改写

Reflect 多数是和Proxy配合食用,后面会说

var proxy = new Proxy(target, handler);
  • proxy接受两个参数,target 为所代理的目标对象,也就是没有Proxy介入时,操作原本要访问的对象
  • 第二参数就是处理函数
get

get(target,key,receiver) 目标对象 属性名 本身 理解一下get的第三个参数 指向proxy实例本身

const proxy = new Proxy({},{
  get: function(target,key,receiver){
    return receiver
  }
})
console.log(proxy.a == proxy)

set

set(obj,prop,value,receiver) 目标对象,属性名 ,属性值,本身

apply

...arguments 其中的一个关键的用法就是 传递参数

var twice = {
  apply (target, ctx, args) {
    console.log(...arguments[2])
    return Reflect.apply(...arguments) * 2;
  }
};
function sum (left, right) {
  return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
construct

(如下)参数:目标对象,构造函数的参数数组 ,new的那个原对象

const handler = {
  construct (target, args, newTarget) {
    return new target(...args);
  }
};

Reflect

  1. 将部分语言内部的属性放置在Reflect上
  2. 与Proxy 一起食用

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。 代码示例

const queueobservable = new Set()
const observe = function(fn){
  return queueobservable.add(fn)
}
const observable = (obj) => new Proxy(obj, {set})
function set(target,key,value,receiver){
  const result = Reflect.set(target,key,value,receiver)
  queueobservable.forEach(observe => observe())
  return result
}
const person = observable({
  name: '张三',
  age: 20
});

function print() {
  console.log(`${person.name}, ${person.age}`)
}

observe(print);
person.name = '李四';
// 输出
// 李四, 20

Promise

一种异步处理的方法

现在应该不怎么用了,用的async awiat 多一点。但面试常考

先上例子,方便理解

const promise = new Promise(function(resolve,reject){
    if(`success`){
        resolve(value)
    }else{
        reject(error)
    }
})
promise.then(function(value){
    // success
},function(error){
    // fail
});

同时需要注意的是Promise函数执行的顺序

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

promise函数遇到则立即执行,而后等待同步代码执行完后,再执行回调函数

示例,说明函数执行的顺序以及规则

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1
new Promise((resolve, reject) => {
  return resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 1

达咩,学不动了,呜呜呜~~,有没有前端的小伙伴交流一下学习心得

Iterator 遍历器

  • 提供一个统一方便的接口,为for of 服务,使得该数据结构能够按照某种次序排序
  • 遍历器对象本质上为指针,调用next方法。向后延展

四种数据结构 Map Set Array Object

  • Symbol.iterator属性,就可以认为是“可遍历的”
  • 原生具备 Iterator 接口的数据结构如下。
    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

类似数组对象,布置Iterator接口,使其可以遍历

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

for of 搭配 Iterator 没有的话 for in

不同于forEach方法,它可以与break、continue和return配合使用。

总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。

Generator 函数的语法

Generator函数是一种状态机函数,还是一个遍历器对象生成函数。返回的遍历器对象可以依次状态机的每一个状态

Generator的相关应用

js为传值调用

Thunk 函数自动执行Generator函数的一种方法 此方法为传名调用

传值调用与传名调用,各有利弊。传值调用比较简单。相当于传入参数前计算其数值。但可能会有一些性能的损失

Thunk函数可以实现Generator的函数的自动执行。因为Generator函数本身是通过调用next方法执行的。通过这种方法将异步的事件化为同步进行执行。 自动控制 Generator 函数的流程,接收和交还程序的执行权


手搓run自动执行Generator函数

function run(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    console.log(result.value)
    next()
  }

  next();
}
var g = function* (){
  var f1 = yield 1;
  var f2 = yield 2;
  // ...
  var fn = yield 3;
};
run(g)

控制台结果:
1
2
3

async

上面讲到的很多东西实际上都是对于异步过程的不断修正。因为JS是一种单线程的脚本语言。

  • 内置执行器 async从外部看跟普通函数完全一致
  • 更好的语义
  • 更好的适应性
  • 返回值为Promise 比Iterator对象稍微好一点

通过代码介绍用法

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});
简易的函数休眠的写法
function timeout(ms){
  return new Promise((resolve) => {
    setTimeout(resolve,ms)
  })
}
async function asyncPoint(value , ms){
  await timeout(ms)
  console.log(value)
}


asyncPoint('helloworld',50)

// async函数的花式写法
// 函数声明
async function foo() {}
// 函数表达式

const foo = async function(){}
// 箭头函数

const foo = async (()=>{
  
})
// async 实现动画的触发,若出现问题则返回上一个动画的返回值
async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}

获取url并按照顺序打印

// fetch 函数为获取url 继发获取url并按照顺序打印
async function readUrl(urls){
  for (const url of urls){
    const response = await fetch(url)
    console.log(await response.text())
  }
}

// 并发获取url并按照顺序打印

async function readUrl(urls){
  const texts = urls.map(async url =>{
    const response = await fetch(url)
    return await response.text()
  })
  for (const text of texts){
    console.log(await text)
  }
}

class

本质上来讲 ES6的类只是ES5构造函数的一种包装。很多的属性都是继承于此

// 代码中的bind相当于讲this指向指向内部
class Logger {
   constructor() {
       
    this.printName = this.printName.bind(this);
    console.log(this.printName)
  }
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}
const logger = new Logger();
const { printName } = logger
console.log(printName())

箭头函数内部的this总是指向定义时所在的对象

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}

const myObj = new Obj();
myObj.getThis() === myObj // true

class 可以通过extends关键字实现继承。相比较之前原型链的方法。better

super()在这里相当于Obj.prototype.constructor.call(this)。

其中需要说明的是将右边的this注入到左边的Obj方法当中,使用call,apply可以劫持别人的想法。(属性和方法),从而实现继承

class Obj{}
class obj extends Obj{
    constructor(){
        super()
    }
}

定义在实例上的方法以及属性是无法通过super获取的

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined

如果属性定义在父类的原型对象上,super就可以取到。

这一点可以推敲一下

class A {}
A.prototype.x = 2;

class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}
let b = new B();
  • 子类必须在constructor方法中调用super方法,因为子类通过父类的构造函数完成构造,获得与父类相同的属性和方法,而后通过添加自身的属性和方法。完成构造。不用super,就得不到this对象。
  • 调用super之前,无法使用this
  • 在子类方法中调用父类方法时,方法内部的this指向子类的方法实例

super.x 在赋值过程中相当于this.x. 而在console.log(super.x) 则相当于A.prototypeof.x

class A {
  constructor() {
    this.x = 1;
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}

let b = new B();

proto 与 prototype

class中的链之间的关系

  • 实例的__proto__总是指向父类,表示构造函数的继承
  • 而B的prototype属性,则表示方法的继承,B.prototype.proto === A.prototype // true。

作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

prototype 意思就是实例

  • 子类的原型的原型就是父类的原型

原生构造函数的继承

原生构造函数是指语言内置的构造函数,通常用来生成数据结构

  • Boolean() Number() String() Array() Date() Function() RegExp() Error() Object()

此方法完全不生效

function MyArray() {
  Array.apply(this, arguments);
}

MyArray.prototype = Object.create(Array.prototype, {
  constructor: {
    value: MyArray,
    writable: true,
    configurable: true,
    enumerable: true
  }
});
const array = new MyArray()
array[0] = 'red'
array.length   // 0

子类无法获得原生构造函数的内部属性,故会出错

class MyArray extends Array{
  constructor(...args){
    super(...args)
  }
}
const array = new MyArray()
array[0] = 1
console.log(array.length)  // 1
  • 所以extends不仅仅可以继承类,还可以继承原生的构造函数
  • 由此你可以构建自身的数据结构

Mixin模式的实现

  • Mixin 指多个对象合并为一个对象。新对象具有各个组成成员的接口

call,apply,bind

  • 用于改变this的指向。
  • 传递参数的区别 call,bind 参数使用,隔开. apply 则为数组对象
  • bind 返回的是一个新的函数 后面加() 才能触发调用

2021-9-14 感觉有的笔记被吞了,我有没有证据 以后写完都要带上日期