前端面试之es6新特性

本文介绍了ES6中的Symbol数据类型,其独特性和转换方法,以及Set、WeakSet、Map和WeakMap四种数据结构的特性、操作和转换。重点讲解了Set的独特性、键值对的Map结构和WeakMap的弱引用机制。
摘要由CSDN通过智能技术生成

1、Symbol

Symbol是es6引入的一种原始数据类型,一种类似字符串的数据类型,表示独一无二的值。Symbol函数不能使用new命令,否则会报错。

javascript其中数据类型: undefined、null、Boolean、String、Number、Object、Sysmol

注:Symbol值不能与其他类型的值进行运算,会报错。

ley sym = Symbol('my symbol);

"your symbol is" + sym 

TypeError: can't convert symbol to string

但是,Symbol值可以显式转为字符串

let sys = Symbol('my symbol');

String(sys); // 'Symbol(My symbol)'

sys.toString(); 'Symbol(My symbol)'

Symbol.prototype.description

const sym = Symbol('foo');

String(sym); // 'Symbol('foo')'

sym.toString();  // 'Symbol('foo')'

sym.description // 'foo'

Symbol可以作为对象的属性名,但是不能用点运算符

let mySymbol = Symbol();

let obj = {
    [mySymbol]: 'hello world'
}

Object.defineProperty(obj, mySymbol, {value: 'hello world'});

obj[mySymbol] // 'hello world'

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

let obj = {};
let a = Symbol('a');

obj[a] = 'hello';

const objectSymbol = Object.getOwnPropertySymbols('obj');

objectSymbol // [Symbol(a)]

Reflect.ownKeys()方法会返回所有类型的键名,包括常规键名和Symbol键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
}

Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]

消除魔术字符串:在代码中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。

例: 

function getArea(shape, options) {
  let area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串


const shapeType = {
  triangle: 'Triangle'
};

// 上述代码中的 'Triangle' 替换成 shapeType.triangle

Symbol.for()、Symbol.keyFor()

Symbol.for(): 可以重新使用同一个Symbol值,Symbol接受一个字符串为参数,然后搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建一个以该字符串作为名称的Symbol值,并将其注册到全局

let s1 = Symbol.for('foo');

let s2 = Symbol.for('foo');

s1 === s2 // true


Symbol.keyFor(s1);  // 'foo'

2、Set、WeakSet、Map、WeakMap 数据结构

1)Set本身是一个构造函数,用来生成Set生成类似数组的数据结构,但成员值都是唯一的,没有重复的值。

set结构的实例有一下属性: add、size、delete、has、clear等方法

let s = new Set([1, 2, 3, 4]);  // 可接受数组作为参数,用来初始化,并去除数组中重复的方法。

s.add(5); // add方法向Set结构加入成员,返回set结构本身

s.size(); // 获取实例的成员总数,

s.delete(value); // 删除某个值, 返回一个Boolean值,表示删除是否成功。

s.has(value); // 返回一个人Boolean布尔值,表示值是否为Set的成员

s.clear(); // 清除所有成员,没有返回值。

遍历操作:

Set.prototype.keys():返回键名的遍历器

Set.prototype.values():返回键值的遍历器

Set.prototype.entries(): 返回键值对的遍历器

Set.prototype.forEach():使用回调函数遍历成员

2)、WeakSet结构与Set类似,也是不重复的值的集合

与Set之间的区别:

WeakSet的成员只能是对象,而不是其他类型的值。

WeakSet中的对象都是弱引用即垃圾回收机制不考虑WeakSet对该对象的引用,即就是其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占的内存,不用考虑该对象是否还存在于WeakSet之中。

3)、Map

Map是类似javascript对象,本质上是键值对的集合(hash结构),键范围不限于字符串,各种类型的值都可以当作键。(传统对象只能用字符串当做键

Map对象的方法:add、get、has、delete等

const map = new Map();

const o = {p: 'hello world'}; 

// 0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

map.set(o, 'content');  // 向map添加一个属性

map.size(); // 1   获取map对象属性的个数

map.get(key);  // 'content'  读取key对应的键值,如果找不到,返回undefined

map.has(key);  // true   是否具有某个属性 返回Boolean

map.delete(key); // true  删除某个属性,返回Boolean

Map的遍历方法

Map.prototype.keys():返回键名的遍历器。

Map.prototype.values():返回键值的遍历器。

Map.prototype.entries():返回所有成员的遍历器。

Map.prototype.forEach():遍历 Map 的所有成员。

Map与其他数据结构的相互转换

Map转为数组:使用扩展运算符(...)

const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

数组转为Map:将数组转入Map构造函数,就可以转化为Map

const map = new Map([[true, 7], ['name';, 'koga']]) // {true: 7, name: 'koga}

Map转为对象:如果Map的键不是字符串,键名会被转换为字符串,再作为对象的键名

function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)   // {yes: true, no: false}

对象转化为Map:对象转为Map 可以通过Object.entries()

let obj = {"a": 1, "b": 2};

let map = new Map(Object.entries(obj));

也可以自己实现一个转换函数

function objToMap(obj){
    let strMap = new Map();
    for (let k of Object.keys(obj)) {
        strMap.set(k, obj[key]);
    }
    return strMap
}

objToMap({yes: true, no: false}); // Map {"yes" => true, "no" => false}

Map 转为 JSON: 分为两种情况

Map的键名都是字符串

function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap);  // '{"yes":true,"no":false}'

Map键名有非字符串,可以转为数组JSON

function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'

JSON转为Map

正常情况下,所有键名都是字符串

function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}

另外特殊情况,整个JSON就是一个数组

function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}

4、WeakMap

weakMap与Map的区别

weakMap只接受对象作为键名(null除外),不能接受其他类型的值作为键名。

weakMap的键名所指向的对象,不计入垃圾回收机制。

WeakMap只有四个方法:

 get()、set()、has()、delete()等

generator和async

const getData = () => new Promise(resolve => {
        setTimeout(() => {
            resolve('data')
        }, 1000)
});

5、generator

function* testG(){
    // await 被编译成了yield
    const data = yield getData();
    console.log('data: ', data);
    const data2 = yield getData();
    console.log('data2: ', data2);
    return 'success';
}
const gen = testG();
const genPromise = gen.next().value;
genPromise.then((value1) => {
    const genPromise2 = gen.next(value1).value;
    genPromise2.then((value2) => {
        const genPromise3 = gen.next(value2).value;
        console.log(genPromise3);
    })
})

6、async/await 

async function test() {
        const data = await getData();
        console.log('data: ', data);
        const data2 = await getData();
        console.log('datas: ', data2);
        return 'success'
}

使用generator实现async

function* testG(){
        // await 被编译成了yield
        const data = yield getData();
        console.log('data: ', data);
        const data2 = yield getData();
        console.log('data2: ', data2);
        return 'success';
}

function asyncToGenerator(generatorFunc){
        return function (){
            // 先调用generator函数 生成迭代器
            // 对应 var gen = testG()
            const gen = generatorFunc.apply(this, arguments);
            // 返回一个promise 因为外部是用.then的方式 或者await的方式去使用这个函数的返回值的
            // var test = asyncToGenerator(testG)
            // test().then(res => console.log(res))
            return new Promise((resolve, reject) => {
                // 内部定义一个step函数 用来一步一步的跨过yield的阻碍
                // key有next和throw两种取值,分别对应了gen的next和throw方法
                // arg参数则是用来把promise resolve出来的值交给下一个yield
                function step(key, arg) {
                    let generatorResult;
                    // 这个方法需要包裹在try catch中
                    // 如果报错了 就把promise给reject掉 外部通过.catch可以获取到错误
                    try {
                        generatorResult = gen[key](arg);
                    }catch (error){
                        return reject(error);
                    }
                    // gen.next() 得到的结果是一个 { value, done } 的结构
                    const {value, done} = generatorResult;
                    if(done){
                        // 如果已经完成了 就直接resolve这个promise
                        // 这个done是在最后一次调用next后才会为true
                        // 以本文的例子来说 此时的结果是 { done: true, value: 'success' }
                        // 这个value也就是generator函数最后的返回值
                        return resolve(value);
                    }else{
                        // 除了最后结束的时候外,每次调用gen.next()
                        // 其实是返回 { value: Promise, done: false } 的结构,
                        // 这里要注意的是Promise.resolve可以接受一个promise为参数
                        // 并且这个promise参数被resolve的时候,这个then才会被调用

                        // 这个value对应的是yield后面的promise

                        // value这个promise被resove的时候,就会执行next
                        // 并且只要done不是true的时候 就会递归的往下解开promise
                        // 对应gen.next().value.then(value => {
                        //    gen.next(value).value.then(value2 => {
                        //       gen.next()
                        //
                        //      // 此时done为true了 整个promise被resolve了
                        //      // 最外部的test().then(res => console.log(res))的then就开始执行了
                        //    })
                        // })
                        return Promise.resolve(value).then((val) => {
                            step('next', val);
                        },
                            // 如果promise被reject了 就再次进入step函数
                            // 不同的是,这次的try catch中调用的是gen.throw(err)
                            // 那么自然就被catch到 然后把promise给reject掉啦
                        (err) => {
                            step('throw', err);
                        })
                    }
                }
                step('next');
            })
        }
    }
    const test = asyncToGenerator(testG);
    test().then((res) => console.log(res))

6、Proxy和Object.defineProperty区别

proxy 的意思是代理,一般叫他拦截器,可以拦截对象上的一个操作。

用法如下:

通过new的方式创建对象,第一个参数是被拦截的对象,第二个参数是对象操作的描述。实例化后返回一个新对象,当我们对这个新对象进行操作时就会调用我们描述中对应的方法。

new Proxy(target, {
    get(target, property) {
        
    },
    set(target, property){
    
    },
    deleteProperty(target, property){

    }
})

Proxy 和 Object.definedProperty的区别:

Object.definedProperty 只能监听到属性的读写。而Proxy除读写外还可以监听属性的删除 ,方法的调用等。通常情况下我们想要见识数组的变化,基本要依赖重写数组方法的方式实现,这也是vue的实现方式,而Proxy可以直接监视数组的变化。

const list = [1, 2, 3];

const list = new Proxy(list, {
    set(target, property, value) {
        target[property] = value;
        return true;
    }
});

Proxy是以非入侵的方式监管了对象的读写,而definedProperty需要按特定的方式定义对象。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值