首页 > 其他分享 >S07-01 JS高级

S07-01 JS高级

时间:2023-03-01 10:37:36浏览次数:35  
标签:mrthis Set console log Symbol S07 01 key JS

JS高级

Symbol

symbol 是一种基本数据类型。

语法

Symbol(description?)

参数

  • descriptionstring,对 symbol 的描述,可用于调试但不是访问 symbol 本身

示例

    // 1. 通过Symbol函数创建一个Symbol
    const s1 = Symbol()

    // 2. 创建的时候传入一个description
    const s2 = Symbol('s2')

    // 3. Symbol函数每次创建出来的值都是独一无二的
    console.log(Symbol() == Symbol()); // falses
    console.log(Symbol() === Symbol()); // false

    // 4. Symbol作为对象属性的标识符
    const obj = {
      [s1]: 'name',
      [s2]: 'age'
    }
    console.log(obj); // {Symbol(): 'name', Symbol(s2): 'age'}

    // 5. 获取Symbol对应的key
    console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(), Symbol(s2)]

    // 6. Symbol.for(key)
    const s3 = Symbol.for('s3')
    console.log(s3) // Symbol(s3)

    // 7. 相同的key,通过`Symbol.for()`可以生成相同的Symbol值
    const s4 = Symbol.for('ss')
    const s5 = Symbol.for('ss')
    console.log(s4 === s5) // true

    // 8. 通过`Symbol.keyFor()` 可以获取通过Symbol.for()传入的key
    console.log(Symbol.keyFor(s2)) // undefined
    console.log(Symbol.keyFor(s5)) // ss

API

  • Symbol(description?):``,创建一个Symbol
  • Object.getOwnPropertySymbols(obj):``,返回一个给定对象自身的所有 Symbol 属性的数组
  • 属性
  • Symbol.prototype.description:``,(只读),返回 Symbol 对象的可选描述的字符串
  • 方法
  • Symbol.for(key) ,会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol
  • Symbol.keyFor(sym):``,用来获取全局 symbol 注册表中与某个 symbol 关联的键key

基本使用

Symbol是什么呢?Symbol是ES6中新增的一个基本数据类型,翻译为符号

那么为什么需要Symbol呢?

  • 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突
  • 比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下,很容易造成冲突,从而覆盖掉它内部的某个属性;
  • 比如我们前面在讲apply、call、bind实现时,我们有给其中添加一个fn属性,那么如果它内部原来已经有了fn属性了呢?
  • 比如开发中我们使用混入,那么混入中出现了同名的属性,必然有一个会被覆盖掉;

Symbol就是为了解决上面的问题,用来生成一个独一无二的值

  • Symbol值是通过Symbol函数来生成的,生成后可以作为属性名;这是该数据类型仅有的目的
  • 也就是在ES6中,对象的属性名可以使用字符串,也可以使用Symbol值

Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的;

我们也可以在创建Symbol值的时候传入一个描述description:这个是ES2019(ES10)新增的特性;

image-20230228160159938

Symbol作为属性名

我们通常会使用Symbol在对象中表示唯一的属性名

image-20230228153141689

相同值的Symbol

前面我们讲Symbol的目的是为了创建一个独一无二的值,那么如果我们现在就是想创建相同的Symbol应该怎么来做呢?

  • 我们可以使用Symbol.for方法来做到这一点
  • 并且我们可以通过Symbol.keyFor方法来获取对应的key

image-20230228153211971

相同的key,通过Symbol.for()可以生成相同的Symbol值

    const s4 = Symbol.for('ss')
    const s5 = Symbol.for('ss')
    console.log(s4 === s5) // true

通过Symbol.keyFor() 可以获取通过Symbol.for()传入的key

    console.log(Symbol.keyFor(s2)) // undefined
    console.log(Symbol.keyFor(s5)) // ss

Set

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

语法

new Set(iterable?)

参数

  • iterable:``,如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set 中

返回值

  • 一个新的 Set 对象

示例


const mySet = new Set();

mySet.add(1); // Set [ 1 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add('some text'); // Set [ 1, 5, 'some text' ]
const o = { a: 1, b: 2 };
mySet.add(o);

API

  • 属性
  • size:``,返回Set中元素的个数
  • 方法
  • add(value)返回:Set对象,添加某个元素
  • delete(value)返回:Boolean,从set中删除和这个值相等的元素
  • has(value)返回:Boolean,判断set中是否存在某个元素
  • clear()返回:void,清空set中所有的元素
  • forEach(callback, thisArg?)返回:undefined,通过forEach遍历set
    • 参数
    • callbackfunction(value?, key?, set?),为集合中每个元素执行的回调函数
    • thisArg ,在执行 callback 时作为 this 使用

注意:Set支持for of的遍历

常见方法

添加元素

    // 2. 添加Set - add()
    set.add('Tom')
    console.log(set); // Set(1) {'Tom'}

    // 3. Set中不能放入重复的元素
    set.add('Jack')
    set.add('Jack')
    console.log(set) // Set(2) {'Tom', 'Jack'}

删除元素

    // 5. 常见方法 - delete()
    console.log(set) // Set(2) {'Tom', 'Jack'}
    set.delete('Tom')
    console.log(set) // Set(1) {'Jack'}

是否包含某个元素

    // 6. 常见方法 - has()
    console.log(set.has('Jack')) // true

清空set

    // 7. 常见方法 - clear()
    set.clear()
    console.log(set) // Set(0) {size: 0}

forEach遍历

    // 8. 常见方法 - forEach()
    set2.forEach((item, index, set) => {
      console.log(item, index, set) // 刘备 刘备 Set(4) {'刘备', '关羽', '张飞', '吕布'}
    })

基本使用

在ES6之前,我们存储数据的结构主要有两种:数组、对象。

  • 在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。

Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复

  • 创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式):

我们可以发现Set中存放的元素是不会重复的,那么Set有一个非常常用的功能就是给数组去重

创建Set

    // 1. 创建Set
    const set = new Set()
    console.log(set) // Set(0) {size: 0}

2个空对象不是重复的元素

    // 10. 2个空对象不是重复的元素
    const set4 = new Set()
    set4.add({})
    set4.add({})
    console.log(set4) // Set(2) {{…}, {…}}

应用:数组去重

    // 4. 应用:数组去重
    const arr = ['刘备', '关羽', '张飞', '吕布', '关羽', '刘备']
    const set2 = new Set(arr)
    console.log(set2) // Set(4) {'刘备', '关羽', '张飞', '吕布'}
    const set3 = Array.from(set2)
    console.log(set3) // (4) ['刘备', '关羽', '张飞', '吕布']

    // 简单写法一
    console.log(Array.from(new Set(arr))) // (4) ['刘备', '关羽', '张飞', '吕布']
    // 或者写法二
    console.log([...new Set(arr)]) // (4) ['刘备', '关羽', '张飞', '吕布']

之前数组去重的做法

image-20230228174503687

set支持for...of遍历

只要是可迭代对象都可以通过for...of遍历

    // 9. 通过for...of遍历Set
    for (const item of set2) {
      console.log(item) // 刘备 关羽 张飞 吕布
    }

WeakSet

WeakSet 对象允许你将弱保持对象存储在一个集合中

语法

API

  • 方法
  • add(value)返回:WeakSet对象,添加某个元素
  • delete(value)返回:Boolean,从WeakSet中删除和这个值相等的元素
  • has(value)返回:Boolean,判断WeakSet中是否存在某个元素

基本使用

和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构

那么和Set有什么区别呢?

  • 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型;
  • 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收;

WeakSet中只能存放对象类型

image-20230228153508949

普通对象的内存图

解释:普通对象被重新赋值为null时,就断开了和内存中对象的联系,但是由于之前已经将对象的内存地址赋值给了数组arr,赋值为null后这些对象依然被数组arr所引用,所以它们并不会被销毁

image-20230301095326167

WeakSet内存图

解释: 添加到WeakSet中的对象都是弱引用,可能会被GC随时回收

image-20230301100631217

注意:WeakSet不能遍历

  • 因为WeakSet只是对对象的弱引用,如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁。

  • 所以存储到WeakSet中的对象是没办法获取的;

应用:限制类中方法的调用者

  • 事实上这个问题并不好回答,我们来使用一个Stack Overflow上的答案;

此处用WeakSet的好处:想要销毁实例对象p的时候,可以直接通过p = null 销毁,如果使用Set的话,由于实例对象一直被Set引用,所以无法销毁

image-20230301101613022

image-20230228153607838

Map

基本使用

另外一个新增的数据结构是Map,用于存储映射关系。

但是我们可能会想,在之前我们可以使用对象来存储映射关系,他们有什么区别呢?

  • 事实上我们对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);

  • 某些情况下我们可能希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key;

那么我们就可以使用Map

image-20230228153713276

常用方法

Map常见的属性:

  • size:返回Map中元素的个数;

Map常见的方法:

  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;
  • clear():清空所有的元素;
  • forEach(callback, [, thisArg]):通过forEach遍历Map;

Map也可以通过for of进行遍历。

WeakMap

基本使用

和Map类型的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的。

那么和Map有什么区别呢?

  • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key;
  • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;

WeakMap常见的方法有四个:

  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;

应用

注意:WeakMap也是不能遍历的

  • 没有forEach方法,也不支持通过for of的方式进行遍历;

那么我们的WeakMap有什么作用呢?(后续专门讲解)

image-20230228153939781

手写

手写call,aplly,bind

函数对象原型关系

函数foo对象的隐式原型 === Function的显式原型

    // 函数foo对象的隐式原型 === Function的显式原型
    console.log(foo.__proto__ === Function.prototype) // true

    console.log(Function.prototype.apply) // f apply() 
    console.log(Function.prototype.call) // f call() 
    console.log(Function.prototype.bind) // f bind() 

    console.log(Function.prototype.apply === foo.apply) // true

结论:

  1. 对象中的某些属性和方法是来自Function.prototype的
  2. 在Function.prototype中添加的属性和方法,可以被所有的函数获取

image-20230224205957711

image-20230224210004633

在Function的原型中添加方法bar

image-20230224211742738

手写apply方法

image-20230224211954891

给函数对象添加方法

image-20230224212210173

    function foo () {
      console.log('foo', this)
    }

    Function.prototype.mrapply = function(mrthis) {
      // 相当于 mrthis.fn = this
      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })
      // 隐式调用fn,可以让fn函数的this指向 mrthis
      mrthis.fn()
      // 删除多出来的临时函数fn
      delete mrthis.fn
    }

    foo.mrapply({name: "Tom"})

如果传入的参数是一个String或者Number的类型,需要将其包裹成对象类型,才能在它上面添加属性

image-20230224214610343

image-20230224214547854

调用mrapply时,传递参数

image-20230224214829699

    function foo (age, height) {
      console.log('foo', this, age, height)
    }

+    Function.prototype.mrapply = function(mrthis, args) {
      // 当this不是对象时,需要用Object包裹
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)

      // 相当于 mrthis.fn = this
      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })

      // 隐式调用fn,可以让fn函数的this指向 mrthis
+      mrthis.fn(...args)

      // 删除多出来的临时函数fn
      delete mrthis.fn
    }

+    foo.mrapply({name: "Tom"}, [18, 1.88])
    foo.mrapply(null, [18, 1.88])
    foo.mrapply(undefined, [18, 1.88])
    foo.mrapply(true, [18, 1.88])
    foo.mrapply(123, [18, 1.88])
    foo.mrapply('aaaa', [18, 1.88])

手写call方法

    function foo(age, height) {
      console.log('foo', this, age, height)
    }

+    Function.prototype.mrcall = function(mrthis, ...args) {
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)

      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })

+      mrthis.fn(...args)

      delete mrthis.fn
    }

+    foo.mrcall({ name: "张飞" }, 20, 1.77)

抽取封装公共函数

    /* 抽取封装的函数 */
+    Function.prototype.mrexec = function(mrthis, args) {
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)
      // mrthis.fn = this
      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })
      mrthis.fn(...args)
      delete mrthis.fn
    }

    /* 手写apply */
    Function.prototype.mrapply = function(mrthis, args) {
      this.mrexec(mrthis, args)
    }

    /* 手写call */
    Function.prototype.mrcall = function(mrthis, ...args) {
      this.mrexec(mrthis, args)
    }

    // 测试
    function foo(age, height) {
      console.log('foo', this, age, height)
    }
    foo.mrapply({name: "Tom"}, [19, 1.66])
    foo.mrcall({name: "Jack"}, 22, 1.99)

手写bind方法

和apply, call不同,bind执行后是返回一个新的函数newFoo

image-20230225114537180

基础实现

思路:想办法实现如下:

// 伪代码
{ name: "why" }.foo(name, age)
    /* 手写bind */
    Function.prototype.mrbind = function(mrthis, ...args) {
+      return (...moreArgs) => {
        mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)
        Object.defineProperty(mrthis, 'fn', {
          configurable: true,
          value: this
        })
+        const allArgs = [...args, ...moreArgs]
+        mrthis.fn(...allArgs)
+        delete mrthis.fn // 可以删除fn,因为每次调用newFoo,都会重新生成一个mrthis.fn
      }
    }

    // 测试
    function foo(name, age, height, address) {
      console.log('foo', this, name, age, height, address)
    }
    const newFoo = foo.mrbind({name: "Jerry"}, '张飞', 45)
    console.log(newFoo)
+    newFoo(1.88, '成都')
+ 	 newFoo(1.88, '成都')

浅拷贝,深拷贝

引用赋值

image-20230228131039985

浅拷贝

方式:

  • 解构赋值:const info = {...obj}

image-20230228131318516

浅拷贝修改info2.name后,obj的name依然是"why",被修改的只是info2

image-20230228131406936

浅拷贝的内存图

image-20230228131822253

如果obj对象中有其他对象(或数组)时的内存图

image-20230228132402319

深拷贝

方式:

  • 1、借助第三方库:underscore
  • 2、利用现有JS机制:JSON
  • 3、自己实现:

2、利用现有JS机制:JSON

语法:

const info3 = JSON.parse(JSON.stringify(obj))

缺点: 该方法不能实现方法的深拷贝,会忽略obj对象中的方法

    const obj = {
      name: 'Tom',
      age: 18,
      friend: {
        name: 'Jack'
      },
      run: function() {
        console.log(this.name + '在跑步~');
      }
    }

    // 利用JSON机制实现深拷贝
+    const info = JSON.parse(JSON.stringify(obj))

    // 测试
    console.log(info)
 	// 修改info的深度属性,obj的深度属性保持不变
+    info.friend.name = '张飞'
+    console.log('obj', obj.friend.name); // obj Jack
+    console.log('info', info.friend.name); // obj 张飞

	// 不能实现方法的深拷贝,会忽略obj对象中的方法
+    info.run() // ncaught TypeError: info.run is not a function

标签:mrthis,Set,console,log,Symbol,S07,01,key,JS
From: https://www.cnblogs.com/pikachu/p/17167175.html

相关文章

  • HTML页面自动清理js、css文件的缓存(自动添加版本号)
    在web项目开发过程中,我们经常会引用css、js文件,更新文件后常出现缓存问题(明明更改了代码,在浏览器上访问的时候却没有发生变化),这种情况我们通常采用以下两种解决方案:1、手......
  • Embedded_driver_note_2012_8_5..8_6
    8/5->relax..规避方法&实践 8/6演示nand完毕 InterleaveOperation K9LCG08U0A-SCBO驱动识别结束 1EC 2ndD3h 3nd51h 4th95h 5th58h FPGAChipScope->.c......
  • FPGA_Xilinx_2012_7_28
    利用Matlab开发FPGA,快速实现复杂算法 ......
  • xilinx_camera 2012_7_28
    图像视频信号处理系统 ......
  • FPGA和CPLD使用时的区别 2012_7_29
    FPGA是现场可编程逻辑门阵列的简称,是电子设计的一个里程碑。CPLD是复杂可变成逻辑器件的简称。尽管FPGA和CPLD都是可编程ASIC器件,有很多共同特点,但由于CPLD和FPGA结构上的......
  • Python 中 response.json 和 json.loads 的区别
    很多时候在python中请求API我都是一会儿用response.json,一会儿用json.loads,但是这两个函数的区别我貌似一直没太搞明白,所以趁着这次就把他们解决掉。问题分析reson......
  • JOISC2019 ふたつの料理 / Two Dishes / 两道料理
    好题!考虑一个暴力DP:\(f(i,j)\)表示前\(i\)个\(A\),前\(j\)个\(B\),最大价值。将\(a,b\)前缀和。令\(A_i=S_i-a_i\),\(B_j\)同理,那么式子化成\(f(i,j)=\max\{f(......
  • day07-MyBatis的关联映射01
    MyBatis的关联映射Mybatis的关联映射实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提......
  • 路飞前台全局css,js文件,安装axios,安装vue-cookies,安装elementui,安装bootstrap和jq,后台
    路飞前台全局css,js文件,安装axios,安装vue-cookies,安装elementui,安装bootstrap和jq,后台主页模块表设计,后台主页模块轮播图接口,录入数据,跨域问题详解路飞前端全局css,js文件......
  • chosen.js
    /*!Chosen,aSelectBoxEnhancerforjQueryandPrototypebyPatrickFillerforHarvest,http://getharvest.comVersion1.3.0Fullsourceathttps://github.com/harv......