JS的基本数据类型

  • String
  • Number
  • Boolean
  • Null
  • Undifined
  • BigInt
  • Symbol
  • Object

Set 和 Map

Set 类似于数组 但是 Set 中没有重复的值 可用于去重

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}


let set = new Set();
set.add(1).add(2) // 1 添加某个值 返回 Set 结构本身
set.size  //2  返回 Set 实例的全部成员
set.has(1) //true 返回一个布尔值,表示该值是否为 Set 的成员
set.delete(1) //删除某个值,返回一个布尔值,表示删除是否成功。
set.clear() //清除所有 Set 成员 没有返回值

ps: WeakSet 结构和 Set 类似 但是 WeakSet 的成员(key值)只能是对象 不能是别的值;

WeakSet 的 key值 还是弱引用 => 垃圾回收机制不考虑 WeakSet 对该对象的引用(如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中)

let set = new WeakSet();
set.add({a:1,b:2}).add({c:1}) // 1 向 WeakSet 实例添加一个新成员
let a = {ss:1}
set.add(a)
set.has({c:1}) //false 返回一个布尔值,表示某个值是否在 WeakSet 实例之中
set.has(a) //true 返回一个布尔值,表示某个值是否在 WeakSet 实例之中
set.delete(a) //清除 WeakSet 实例的指定成员。

Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Object 提供了 “字符串-值” 的集合 Map 提供了 “值-值”的集合

Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。

  const map = new Map();

  const k1 = ['a'];
  const k2 = ['a'];

  map
  .set(k1, 111)
  .set(k2, 222);

  map.get(k1) // 111
  map.get(k2) // 222

  const map = new Map();
  map.set(1, 'a')
  .set(2, 'b')
  .set(3, 'c'); // set方法设置键名key对应的键值为value,然后返回整个 Map 结构。
  map.size//3 size属性返回 Map 结构的成员总数
  map.get(1) // a  get方法读取key对应的键值,如果找不到key,返回undefined
  map.has(2)  //true  has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
  map.delete(3) //true delete方法删除某个键,返回true。如果删除失败,返回false。
  map.clear() //clear方法清除所有成员,没有返回值。

JavaScript 的面向对象编程

对象:

  • 对单个实物的抽象
  • 对象是一个容器,封装了属性(property)和方法(method)

典型的面向对象编程语言(如C++ 和Java),都有“类”这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是JavaScript语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。JavaScript使用构造函数作为对象的模板。

构造函数:

  • 函数体内部使用了 this 关键字,代表了索要生成的对象实例
  • 生成对象的时候,必须使用new 命令

new 命令

  • 创建一个空的对象,作为将要返回的对象实例
  • 将这个空对象的原型,指向构造函数的prototy 属性
  • 将这个空对象赋值给函数内部的 this 关键字
  • 开始执行构造函数内部的代码

原型和原型链

原型和原型链都是来源于对象 并且 服务于对象的概念

JavaScript 中所有引用类型都是对象 ( Array类型、Function类型、Object类型、Date类型、RegExp类型 等都是引用类型 也就是对象)对象就是属性(property)的集合

  let obj = {}
  //给 obj 对象添加一个方法
  obj.sayHello = function(){
    console.log("111")
  }
  obj.hasOwnProperty('sayHello') //true obj上确实存在一个 sayHello 的方法

但是 obj 上并没有 hasOwnProperty 这个方法 为什么可以调用呢?

每一个对象从创建开始就和另一个对象相关联,从另一个对象上继承其属性,这另一个对象就是原型

当访问一个对象的属性时,先在对象的本身上找,找不到该属性时就去对象的原型上去找,还是找不到的话,就去对象的原型的原型上找,直至找到;或者查找到最顶层的原型对象中也没找到就停止查找,返回undifined

原型链就是这条由对象和对象的原型所组成的链

数组使用 push() 和 slice() 等方法也是因为在数组的原型链上找到了对应的方法

总结一下:

1、原型存在的意义就是组成原型链 => 引用类型皆是对象,每个对象都有原型,每个原型也是对象,他也有自己的原型,一层一层组成原型链。

2、原型链存在的意义就是继承 => 当在一个对象上访问属性,现在对象自身上找,找不到再去对象的原型上找,还找不到就到对象的原型的原型上查找,直到查找成功;如果到最顶层的原型上也查找不到的话,就返回undifined。在原型链上一层层查找。

3、继承存在的意义就是属性共享 => 继承的好处 一是代码复用,二是可扩展。对象可以有统一的继承下来的属性,也可以有自身新建的属性

构造函数用来创建对象,同一构造函数创建出的对象,他们的原型都相同

  function Person(name){
    this.name = name
    this.sayHello= function(){
      console.log(this.name)
    }
  }
  
  let obj = new Person("zhangsan") //newObject => 构造函数
  obj.sayHello() //zhangsan

对象的 "proto" 属性就指向对象原型

   Object.getPrototypeOf(person) === person.__proto__ // true 对象的 "__proto__" 属性就指向对象原型
   __proto__ 是所有引用类型都有的属性 均指向对象的原型
   
   prototype 是只有函数才有的属性 指向 new 操作符加函数调用所创建的对象实例的对象原型
   

alt

原型链之所以不是原型环而是原型链 是因为原型链有始有终

let person = new Person()
person.__proto__ ===  Person.prototype // true newObject是函数 也是引用类型 也有原型对象
Person.prototype.__proto__ === Object.prototype // 函数的原型对象就是Object.prototype
Object.prototype.__proto__ === null // Object.prototype没有原型对象 也就是原型链的尽头

function getProperty(obj, propName) {
    // 在对象本身查找
    if (obj.hasOwnProperty(propName)) {
        return obj[propName]
    } else if (obj.__proto__ !== null) {
    // 如果对象有原型,则在原型上递归查找
        return getProperty(obj.__proto__, propName)
    } else {
    // 直到找到Object.prototype,Object.prototype.__proto__为null,返回undefined
        return undefined
    }
}

构造函数有"prototype"属性用来指向函数的原型对象 同时 函数的原型对象也有一个属性"constructor" 用来直接指向构造函数

     Person.prototype.constructor === Person // true

alt

函数都是由Function原生构造函数创建的,所以函数的__proto__属性指向Function的prototype属性

  let fn = function() {}
  // 函数(包括原生构造函数)的原型对象为Function.prototype
  fn.__proto__ === Function.prototype // true
  Array.__proto__ === Function.prototype // true
  Object.__proto__ === Function.prototype // true

alt

    // f1、f2都是通过new Foo()创建的对象,构造函数为Foo,所以有
    f1.__proto__ === Foo.prototype
    f2.__proto__ === Foo.prototype
    // Foo.prototype为普通对象,构造函数为Object,所以有
    Foo.prototype.__proto__ === Object.prototype
    // Object.prototype没有原型对象
    Object.prototype.__proto__ === null
    
    // Foo是个函数对象,构造函数为Function
    Foo.__proto__ = Function.prototype
    // Function.prototype为普通对象,构造函数为Object,所以有
    Function.prototype.__proto__ === Object.prototype
    
    // o1、o2构造函数为Object
    o1.__proto__ === Object.prototype
    
    // 原生构造函数也是函数对象,其构造函数为Function
    Object.__proto__ === Function.prototype
    // 特例
    Function.__proto__ === Function.prototype

this

方法是谁调用的 this就指向谁

JavaScript实现继承共6种方式: 原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承。

apply,call,bind的用法和区别(方法劫持)

用途:手动改变this的指向

区别:1.apply和call会使当前函数立即执行,bind会返回一个函数,后续需要时再调用

  1. call是apply的语法糖,只有传的参数不同,call中要传多个任意参数,apply只可以直接传数组或者类数组

  2. bind是为函数绑定一个this上下文

规则: fn.apply(上下文环境,执行所需数组)

 fn.call(上下文环境,执行所需单个参数)

 fn.bind(上下文环境)

ps:如果上下文的值为null,则使用全局对象代替,相当于没传上下文还用以前的

apply可以将一个数组转换为一个参数列表([p1,p2,p3]转换为p1,p2,p3)

var arr=[1,2,3,4]

console.log (Math.max.apply(null,arr))//4

Math.max()只能传数字,可以使用apply将数组转为一个一个参数传入