es6复习过程中遇到的常用知识点

1、变量解构赋值的用途  地址

(1)交换变量的值

(2)从函数返回多个值

(3)函数参数的定义

(4)提取 JSON 数据

(5)函数参数的默认值

(6)遍历 Map 结构

(7)输入模块的指定方法

2、字符串中常用的方法  地址

3、数组的扩展 地址

4、对象的一些常用方法  地址

5、Symbol的使用 地址

symbol的用途

属性名的遍历

Symbol.for()和Symbol.keyFor()

内置的symbol值--11个

6、Set和Map

7、Proxy的基本使用和它在vuejs3.0中的应用(Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty--面试)


 

 

待续。。

1、变量解构赋值的用途  地址

(1)交换变量的值

let x = 1;
let y = 2;

[x, y] = [y, x];

(2)从函数返回多个值

// 返回一个数组

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象

function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

(3)函数参数的定义

// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);

// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

(4)提取 JSON 数据

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

(5)函数参数的默认值

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};

(6)遍历 Map 结构

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world



// 获取键名
for (let [key] of map) {
  // ...
}

// 获取键值
for (let [,value] of map) {
  // ...
}

(7)输入模块的指定方法

const { SourceMapConsumer, SourceNode } = require("source-map");

2、字符串中常用的方法  地址

这里只是一小部分我用到的,更详细的请到地址所在链接去看

1)includes(), startsWith(), endsWith()

可以用来确定一个字符串是否包含在另一个字符串中。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true


//这三个方法都支持第二个参数,表示开始搜索的位置
let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

2)padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'


//padStart()的常见用途是为数值补全指定位数
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"


//另一个用途是提示字符串格式
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

3)trimStart(),trimEnd() ,trim()

ES2019 对字符串实例新增了trimStart()trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。

浏览器还部署了额外的两个方法,trimLeft()trimStart()的别名,trimRight()trimEnd()的别名

3、数组的扩展 地址

1)扩展运算符(...),个人感觉用好它,能省好多事(仅仅记录了我工作中用到的,更详细的请到地址链接去查看)

**复制数组

const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;

**合并数组

//注意都是浅拷贝

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

**与解构赋值结合

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

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

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

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

const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错

** Map 和 Set 结构(提到Set,顺嘴提一下,使用Array.from()+new Set()实现数组去重效果不错哦!前者负责将set对象转为数组,后者负责去重。)

let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let arr = [...map.keys()]; // [1, 2, 3]

 **Array.from---将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。

const toArray = (() =>
  Array.from ? Array.from : obj => [].slice.call(obj)
)();
//Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
//如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this。

Array.from([1, 2, 3], (x) => x * x)

// [1, 4, 9]

**Array.of--用于将一组值,转换为数组

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

Array.of基本上可以用来替代Array()new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一

**数组实例的 find() 和 findIndex()

[1, 4, -5, 10].find((n) => n < 0)
// -5

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

1)数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined

2)数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

3)这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); 

//find函数接收了第二个参数person对象,回调函数中的this对象指向person对象。

4) 这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足。

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

**数组实例的 entries(),keys() 和 values()

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

ES6 提供三个新的方法——entries()keys()values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

**数组实例的 includes()---用来替换indexOf

//一个参数

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

//两个参数

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

//es5

if (arr.indexOf(el) !== -1) {
  // ...
}

1)Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。

2)该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

3)indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判

[NaN].indexOf(NaN) // -1

[NaN].includes(NaN)  // true,includes使用的是不一样的判断算法,就没有这个问题

4) Map 和 Set 数据结构有一个has方法,需要注意与includes区分

  • Map 结构的has方法,是用来查找键名的,比如Map.prototype.has(key)WeakMap.prototype.has(key)Reflect.has(target, propertyKey)
  • Set 结构的has方法,是用来查找值的,比如Set.prototype.has(value)WeakSet.prototype.has(value)

4、对象的一些常用方法  地址

var obj = { foo: 'bar', baz: 42 };


Object.keys(obj)
// ["foo", "baz"]


Object.values(obj)
// ["bar", 42]

Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

将对象转为Map结构

const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

值对的数据结构还原为对象:Object.fromEntries()

// 例一
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }

将查询字符串转为i数组:URLSearchParams对象+Object.fromEntries()

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }

5、Symbol的使用 地址

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

let s = Symbol("foo"); //foo作为Symbol的参数(可不传),是对Symbol的描述。主要是为了在控制台输出时的区别
let s1 = Symbol("fool")

// Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
console.log(s === s1) //false
console.log(typeof s)  //symbol
console.log(s.description ) //foo  //es2019提出

// Symbol 值可以显式转为字符串,布尔值,但是不能转换为数值
console.log(String(s),Boolean(s)) //Symbol(foo) true
console.log(Number(s)) //Uncaught TypeError: Cannot convert a Symbol value to a number

// Symbol 值不能与其他类型的值进行运算,会报错
let str = ""
console.log(str+s) //ncaught TypeError: Cannot convert a Symbol value to a string

Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型

symbol的用途

**作为属性名--由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性

let str = Symbol()
let strf = Symbol()
// let a = {}
// a[str] = 'test'
//或
// let a = {}
// Object.defineProperty(a,str,{value:"test"})
// 或
let a = {
    [str]: 'test',
    [strf]() {
         console.log("haha")
    }
}

a[strf]() //haha
console.log(a[str]) //test

// Symbol 值作为对象属性名时,不能用点运算符
let str1 = Symbol()
a.str1 = "test1"
console.log(a['str1']) //test1
console.log(a[str1]) //undefined

// 用来定义自足常量,保证常用的值都是不相等的
const log = {}
log.levels = {
     DEBUG: Symbol('debug'),
     INFO: Symbol('info'),
     WARN: Symbol('warn')
};
console.log(log.levels.DEBUG, 'debug message'); //Symbol(debug) "debug message"
console.log(log.levels.INFO, 'info message'); //Symbol(info) "info message"

点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值

属性名的遍历

Object.getOwnPropertySymbols或者Reflect.ownKeys

const obj = {}
let a = Symbol('a')
let b = Symbol('b')
obj[a] = 'hello'
obj[b] = 'world'
const objSymbol = Object.getOwnPropertySymbols(obj)
console.log(objSymbol)  //[Symbol(a), Symbol(b)]
console.log(Reflect.ownKeys(obj)) [Symbol(a), Symbol(b)]

Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

Symbol.for()和Symbol.keyFor()

// 使用Symbol函数返回值不会相同,使用Symbol.for可以得到相同的Symbol值,且登记的名字是全局的。
let s1 = Symbol.for("foo")
let s2 = Symbol.for("foo")
console.log(s1 === s2) //true
console.log(Symbol.keyFor(s1)) //foo  返回一个已登记的 Symbol 类型值的key

Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。

Symbol()写法没有登记机制,所以每次调用都会返回一个不同的值

内置的symbol值--11个

**Symbol.hasInstance---指向一个内部方法

**Symbol.isConcatSpreadable---等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开

**Symbol.species---指向一个构造函数。创建衍生对象时,会使用该属性

**Symbol.match---指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值

**Symbol.replace---指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值

**Symbol.search---指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值

**Symbol.split---指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值

**Symbol.iterator---指向该对象的默认遍历器方法

**Symbol.toPrimitive---指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值

**Symbol.toStringTag---指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串

**Symbol.unscopables---指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除

6、Set和Map

set我常用来做去重处理

// set结构--由于set的值是惟一的所以可以用它来去重
        let arg1 = [1, 1, 1, 2, 3]
        let arg2 = "ababcdabab"
        console.log([...new Set(arg1)])  //数组去重
        console.log([...new Set(arg2)].join("")) //字符串去重

        // 对象去重
        let arg = [{
            id: 1,
            text: "11"
        }, {
            id: 1,
            text: "11"
        }]
        let quchong = (arg) => {
            var unique = {};
            arg.forEach(function (gpa) {
                unique[JSON.stringify(gpa)] = gpa
            });
            arg = Object.keys(unique).map(function (u) {
                return JSON.parse(u)
            });
            return Object.values(unique)
        }
        console.log(quchong(arg))

set实例的属性和方法

 // 操作方法: add,delete,has,clear
        let arg = [1, 2, 3, 4, 5, 6]
        let testSet = new Set(arg)
        testSet.add(7) //添加某个值
        console.log(testSet)
        testSet.delete(7) //删除某个值
        console.log(testSet)
        console.log(testSet.has(1)) //判断是否有某个值
        testSet.clear() //清除所有值,没有返回值
        console.log(testSet)

        let arg2 = [{
            id: parseInt(Math.random()),
            text: "001"
        }, {
            id: parseInt(Math.random()),
            text: "002"
        }]
        let testSet2 = new Set(arg2)
        // 遍历操作--keys,values,entries,forEach
        // 由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致
        console.log(testSet2.keys())
        console.log(testSet2.values())
        console.log(testSet2.entries())
        testSet2.forEach((value, key) => {
            console.log(value, key)  //key和value值一样
        }, this)
        // 使用for...of遍历
        for (let item of testSet2) {
            console.log(item)
        }

        // 在遍历中改变set原有的值,目前可通过以下两种间接实现
        let testSet3 = new Set([1,2,3,4,5,6]);
        testSet3 = new Set([...testSet3].map(val => val*3))
        console.log(testSet3)
        //或
        testSet3 = new Set(Array.from(testSet3,val => val/3))
        console.log(testSet3)

map的话,这边暂时没有用到的实例,了解的话可以到https://es6.ruanyifeng.com/#docs/set-map

7、Proxy的基本使用和它在vuejs3.0中的应用(Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty--面试)

MDN上对proxy的定义为:Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

比起这个定义我更喜欢es6上对它的跟该直白解释:Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

然而这些都可以用vue中的一句话概括:Proxy 是一个包含另一个对象或函数并允许你对其进行拦截的对象

proxy使用的语法:var proxy = new Proxy(target(索要拦截的目标对象), handler(也是对象,用来定制拦截行为));

基本使用:

let handler = {
        get(target, propKey) {
            return prop in target ? target[propKey] : 37;
        }
    }
    let p = new Proxy({}, handler);
    p.a = 1;
    p.b = undefined;
    console.log(p.a, p.b) //1,undefined
    console.log('c' in p, p.c)//false,37

Proxy 支持的拦截操作一览--13,具体的使用案例,如果你对proxy由一定的了解可以到https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy,这里介绍的更加简介

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

看到proxy这一篇,突然想到了之前看到的一个面试题:参考文档,这里包括了vue中使用的解说

Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty(其实并没有抛弃,Vue 3 版本也使用了 Object.defineProperty 来支持 IE 浏览器。两者具有相同的 Surface API,但是 Proxy 版本更精简,同时提升了性能)

Object.defineProperty:

**Vue 无法检测到 property 的添加或删除

**Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

**Vue 不允许动态添加根级响应式 property,所以你必须在初始化实例前声明所有根级响应式 property,哪怕只是一个空值

proxy:

**可以劫持整个对象,并返回一个新对象
**有13种劫持操作

猜你喜欢

转载自blog.csdn.net/qq_41687299/article/details/112167405