重温基础:ES6系列(三)

640?wx_fmt=png

ES6系列目录

  • 1 let 和 const命令

  • 2 变量的解构赋值

  • 3 字符串的拓展

  • 4 正则的拓展

  • 5 数值的拓展

  • 6 函数的拓展

  • 7 数组的拓展

  • 对象的拓展

  • Symbol

  • 10 Set和Map数据结构

  • 11 Proxy

  • 12 Promise对象

  • 13 Iterator和 for...of循环

  • 14 Generator函数和应用

  • 15 Class语法和继承

  • 16 Module语法和加载实现


所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com

8 对象的拓展

8.1 属性的简洁表示

let a = 'a1';	
let b = { a };  // b => { a : 'a1' }	
// 等同于	
let b = { a : a };	
function f(a, b){	
    return {a, b}; 	
}	
// 等同于	
function f (a, b){	
    return {a:a ,b:b};	
}	
let a = {	
    fun () {	
        return 'leo';	
    }	
}	
// 等同于	
let a = {	
    fun : function(){	
        return 'leo';	
    }	
}

8.2 属性名表达式

JavaScript提供2种方法定义对象的属性

// 方法1 标识符作为属性名	
a.f = true;	
// 方法2 字符串作为属性名	
a['f' + 'un'] = true;

延伸出来的还有:

let a = 'hi leo';	
let b = {	
    [a]: true,	
    ['a'+'bc']: 123,	
    ['my' + 'fun'] (){	
        return 'hi';	
    }	
};	
// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'	
// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }

注意

// 报错	
let a1 = 'aa';	
let a2 = 'bb';	
let b1 = {[a1]};	
// 正确	
let a1 = 'aa';	
let b1 = { [a1] : 'bb'};

8.3 Object.is()

Object.is() 用于比较两个值是否严格相等,在ES5时候只要使用相等运算符( ==)和严格相等运算符( ===)就可以做比较,但是它们都有缺点,前者会自动转换数据类型,后者的 NaN不等于自身,以及 +0等于 -0

Object.is('a','a');   // true	
Object.is({}, {});    // false	
// ES5	
+0 === -0 ;           // true	
NaN === NaN;          // false	
// ES6	
Object.is(+0,-0);     // false	
Object.is(NaN,NaN);   // true

8.4 Object.assign()

Object.assign()方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。基础用法目标对象,后面参数都是源对象

let a = {a:1};	
let b = {b:2};	
Object.assign(a,b);  // a=> {a:1,b:2}

注意

  • 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。

let a = {a:1, b:2};	
let b = {b:3, c:4};	
Object.assign(a, b); // a => {a:1, b:3, c:4}
  • 若只有一个参数,则返回该参数。

let a = {a:1};	
Object.assign(a) === a;  // true
  • 若参数不是对象,则先转成对象后返回。

typeof Object.assign(2); // 'object'
  • 由于 undefined或 NaN无法转成对象,所以做为参数会报错。

Object.assign(undefined) // 报错	
Object.assign(NaN);      // 报错
  • Object.assign()实现的是浅拷贝。

Object.assign()拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

let a = {a: {b:1}};	
let b = Object.assign({},a);	
a.a.b = 2;	
console.log(b.a.b);  // 2
  • 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。

Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]

9 Symbol

9.1 介绍

ES6引入 Symbol作为一种新的原始数据类型,表示独一无二的值,主要是为了防止属性名冲突SymbolundefinednullBooleanStringNumberObject

let a = Symbol();	
typeof a; // "symbol"

注意:

  • Symbol函数不能用 new,会报错。由于 Symbol是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。

  • Symbol都是不相等的,即使参数相同。

// 没有参数	
let a1 = Symbol();	
let a2 = Symbol();	
a1 === a2; // false 	
// 有参数	
let a1 = Symbol('abc');	
let a2 = Symbol('abc');	
a1 === a2; // false 
  • Symbol不能与其他类型的值计算,会报错。

let a = Symbol('hello');	
a + " world!";  // 报错	
`${a} world!`;  // 报错

Symbol可以显式转换为字符串:

let a1 = Symbol('hello');	
String(a1);    // "Symbol(hello)"	
a1.toString(); // "Symbol(hello)"

Symbol可以转换为布尔值,但不能转为数值:

let a1 = Symbol();	
Boolean(a1);	
!a1;        // false	
Number(a1); // TypeError	
a1 + 1 ;    // TypeError

9.2 Symbol作为属性名

好处:防止同名属性,还有防止键被改写或覆盖。

let a1 = Symbol();	
// 写法1	
let b = {};	
b[a1] = 'hello';	
// 写法2	
let b = {	
    [a1] : 'hello'	
} 	
// 写法3	
let b = {};	
Object.defineProperty(b, a1, {value : 'hello' });	
// 3种写法 结果相同	
b[a1]; // 'hello'

需要注意: Symbol作为对象属性名时,不能用点运算符,并且必须放在方括号内。

let a = Symbol();	
let b = {};	
// 不能用点运算	
b.a = 'hello';	
b[a] ; // undefined	
b['a'] ; // 'hello'	
// 必须放在方括号内	
let c = {	
    [a] : function (text){	
        console.log(text);	
    }	
}	
c[a]('leo'); // 'leo'	
// 上面等价于 更简洁	
let c = {	
    [a](text){	
        console.log(text);	
    }	
}

常常还用于创建一组常量,保证所有值不相等:

let a = {};	
a.a1 = {	
    AAA: Symbol('aaa'),	
    BBB: Symbol('bbb'),	
    CCC: Symbol('ccc')	
}

9.3 应用:消除魔术字符串

魔术字符串:指代码中多次出现,强耦合的字符串或数值,应该避免,而使用含义清晰的变量代替。

function f(a){	
    if(a == 'leo') {	
        console.log('hello');	
    }	
}	
f('leo');   // 'leo' 为魔术字符串

常使用变量,消除魔术字符串:

let obj = {	
    name: 'leo'	
};	
function f (a){	
    if(a == obj.name){	
        console.log('hello');	
    }	
}	
f(obj.name); // 'leo'

使用Symbol消除强耦合,使得不需关系具体的值:

let obj = {	
    name: Symbol()	
};	
function f (a){	
    if(a == obj.name){	
        console.log('hello');	
    }	
}	
f(obj.name);

9.4 属性名遍历

Symbol作为属性名遍历,不出现在 for...infor...of循环,也不被 Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

let a = Symbol('aa'),b= Symbol('bb');	
let obj = {	
    [a]:'11', [b]:'22'	
}	
for(let k of Object.values(obj)){console.log(k)}	
// 无输出	
let obj = {};	
let aa = Symbol('leo');	
Object.defineProperty(obj, aa, {value: 'hi'});	
for(let k in obj){	
    console.log(k); // 无输出	
}	
Object.getOwnPropertyNames(obj);   // []	
Object.getOwnPropertySymbols(obj); // [Symbol(leo)]

Object.getOwnPropertySymbols方法返回一个数组,包含当前对象所有用做属性名的Symbol值。

let a = {};	
let a1 = Symbol('a');	
let a2 = Symbol('b');	
a[a1] = 'hi';	
a[a2] = 'oi';	
let obj = Object.getOwnPropertySymbols(a);	
obj; //  [Symbol(a), Symbol(b)]

另外可以使用 Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

let a = {	
    [Symbol('leo')]: 1,	
    aa : 2, 	
    bb : 3,	
}	
Reflect.ownKeys(a); // ['aa', 'bb',Symbol('leo')]

由于Symbol值作为名称的属性不被常规方法遍历获取,因此常用于定义对象的一些非私有,且内部使用的方法。

9.5 Symbol.for()、Symbol.keyFor()

  • Symbol.for()用于重复使用一个Symbol值,接收一个字符串作为参数,若存在用此参数作为名称的Symbol值,返回这个Symbol,否则新建并返回以这个参数为名称的Symbol值。

let a = Symbol.for('aaa');	
let b = Symbol.for('aaa');	
a === b;  // true

Symbol()Symbol.for()区别:

Symbol.for('aa') === Symbol.for('aa'); // true	
Symbol('aa') === Symbol('aa');         // false
  • Symbol.keyFor()用于返回一个已使用的Symbol类型的key:

let a = Symbol.for('aa');	
Symbol.keyFor(a);   //  'aa'	
let b = Symbol('aa');	
Symbol.keyFor(b);   //  undefined

9.6 内置的Symbol值

ES6提供11个内置的Symbol值,指向语言内部使用的方法:

  • 1.Symbol.hasInstanceinstanceof运算符,判断是否为该对象的实例时,会调用这个方法。比如, fooinstanceofFoo在语言内部,实际调用的是 Foo[Symbol.hasInstance](foo)

class P {	
    [Symbol.hasInstance](a){	
        return a instanceof Array;	
    }	
}	
[1, 2, 3] instanceof new P(); // true

P是一个类,new P()会返回一个实例,该实例的 Symbol.hasInstance方法,会在进行 instanceof运算时自动调用,判断左侧的运算子是否为 Array的实例。

  • 2.Symbol.isConcatSpreadableArray.prototype.concat()时,是否可以展开。

let a = ['aa','bb'];	
['cc','dd'].concat(a, 'ee'); 	
// ['cc', 'dd', 'aa', 'bb', 'ee']	
a[Symbol.isConcatSpreadable]; // undefined	
let b = ['aa','bb']; 	
b[Symbol.isConcatSpreadable] = false; 	
['cc','dd'].concat(b, 'ee'); 	
// ['cc', 'dd',[ 'aa', 'bb'], 'ee']
  • 3.Symbol.speciesget取值器。

class P extends Array {	
    static get [Symbol.species](){	
        return this;	
    }	
}

解决下面问题:

// 问题:  b应该是 Array 的实例,实际上是 P 的实例	
class P extends Array{}	
let a = new P(1,2,3);	
let b = a.map(x => x);	
b instanceof Array; // true	
b instanceof P; // true	
// 解决:  通过使用 Symbol.species	
class P extends Array {	
  static get [Symbol.species]() { return Array; }	
}	
let a = new P();	
let b = a.map(x => x);	
b instanceof P;     // false	
b instanceof Array; // true
  • 4.Symbol.matchstr.match(myObject),传入的属性存在时会调用,并返回该方法的返回值。

class P {	
    [Symbol.match](string){	
        return 'hello world'.indexOf(string);	
    }	
}	
'h'.match(new P());   // 0
  • 5.Symbol.replace 当该对象被 String.prototype.replace方法调用时,会返回该方法的返回值。

let a = {};	
a[Symbol.replace] = (...s) => console.log(s);	
'Hello'.replace(a , 'World') // ["Hello", "World"]
  • 6.Symbol.hasInstanceString.prototype.search方法调用时,会返回该方法的返回值。

class P {	
    constructor(val) {	
        this.val = val;	
    }	
    [Symbol.search](s){	
        return s.indexOf(this.val);	
    }	
}	
'hileo'.search(new P('leo')); // 2
  • 7.Symbol.splitString.prototype.split方法调用时,会返回该方法的返回值。

// 重新定义了字符串对象的split方法的行为	
class P {	
    constructor(val) {	
        this.val = val;	
    }	
    [Symbol.split](s) {	
        let i = s.indexOf(this.val);	
        if(i == -1) return s;	
        return [	
            s.substr(0, i),	
            s.substr(i + this.val.length)	
        ]	
    }	
}	
'helloworld'.split(new P('hello')); // ["hello", ""]	
'helloworld'.split(new P('world')); // ["", "world"] 	
'helloworld'.split(new P('leo'));   // "helloworld"
  • 8.Symbol.iteratorfor...of循环时,会调用 Symbol.iterator方法,返回该对象的默认遍历器。

class P {	
    *[Symbol.interator]() {	
        let i = 0;	
        while(this[i] !== undefined ) {	
            yield this[i];	
            ++i;	
        }	
    }	
}	
let a = new P();	
a[0] = 1;	
a[1] = 2;	
for (let k of a){	
    console.log(k);	
}
  • 9.Symbol.toPrimitive

    • Number : 此时需要转换成数值

    • String : 此时需要转换成字符串

    • Default : 此时可以转换成数值或字符串

let obj = {	
  [Symbol.toPrimitive](hint) {	
    switch (hint) {	
      case 'number':	
        return 123;	
      case 'string':	
        return 'str';	
      case 'default':	
        return 'default';	
      default:	
        throw new Error();	
     }	
   }	
};	
2 * obj // 246	
3 + obj // '3default'	
obj == 'default' // true	
String(obj) // 'str'
  • 10.Symbol.toStringTagObject.prototype.toString方法时,如果这个属性存在,它的返回值会出现在 toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制 [objectObject]或 [objectArray]中 object后面的那个字符串。

// 例一	
({[Symbol.toStringTag]: 'Foo'}.toString())	
// "[object Foo]"	
// 例二	
class Collection {	
  get [Symbol.toStringTag]() {	
    return 'xxx';	
  }	
}	
let x = new Collection();	
Object.prototype.toString.call(x) // "[object xxx]"
  • 11.Symbol.unscopables

// 没有 unscopables 时	
class MyClass {	
  foo() { return 1; }	
}	
var foo = function () { return 2; };	
with (MyClass.prototype) {	
  foo(); // 1	
}	
// 有 unscopables 时	
class MyClass {	
  foo() { return 1; }	
  get [Symbol.unscopables]() {	
    return { foo: true };	
  }	
}	
var foo = function () { return 2; };	
with (MyClass.prototype) {	
  foo(); // 2	
}

上面代码通过指定 Symbol.unscopables属性,使得 with语法块不会在当前作用域寻找 foo属性,即 foo将指向外层作用域的变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值