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 操作符加函数调用所创建的对象实例的对象原型
原型链之所以不是原型环而是原型链 是因为原型链有始有终。
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
函数都是由Function原生构造函数创建的,所以函数的__proto__属性指向Function的prototype属性
let fn = function() {}
// 函数(包括原生构造函数)的原型对象为Function.prototype
fn.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
// 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会返回一个函数,后续需要时再调用
-
call是apply的语法糖,只有传的参数不同,call中要传多个任意参数,apply只可以直接传数组或者类数组
-
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将数组转为一个一个参数传入