JS高级的学习(二)
set对象
- Set 是一个对象 存放数据 数据永远不会重复
- Set 当成是一个数组 遍历 使用 数组方法 find findIndex Map
- 数组转成 Set对象 const set = new Set([])
- Set对象转成 数组 const arr=[…set]
// 旧的数组
const list = [1, 2, 3, 4, 5]
// 1.Set对象 需要被 new 出来使用
// const set = new Set()
const set = new Set(list)
// 2.存放数据 调用 add方法
set.add(7)
set.add(8)
set.add(8)
set.add(9)
console.log(set)
// 3.把set对象 转成数组
const arr = [...set]
console.log(arr)
创建对象的n种方式
- 字面量 不方便维护,修改
- 工厂函数 封装 继承,参数一多也很麻烦
- 重点介绍 构造函数
// 创建对象的方式:
// 字面量 不方便维护 - 修改
// const obj = { nickname: '悟空', height: 190 }
// const obj1 = { nickname: '八戒', height: 100 }
// const obj2 = { nickname: '沙和尚', height: 80 }
// 工厂函数 封装 继承,参数一多也很麻烦
function person(name, height, a, b, c, d, e) {
return {
nickname: name,
height,
a,
b,
c,
d,
e,
}
}
const obj = person('悟空', 190)
const obj1 = person('八戒', 100)
const obj2 = person('沙和尚', 80)
console.log(obj)
console.log(obj1)
console.log(obj2)
// 最重要的是 构造函数
箭头函数 没有this指向
- 箭头函数没有内部的this
- 当函数执行体代码中 有出现了 this 慎用 箭头函数
const obj = {
username: "悟空",
say: () => console.log(this) // this指向window
}
const funct1 = () => console.log(this) // this指向window
obj.say()
funct1()
const button = document.querySelector("button")
button.addEventListener("click", function () {
this.innerText = "被修改了哈哈"
console.log(this) // 按钮本身
})
button.addEventListener("click", () => {
// this.innerText = "被修改哈" // error,this指向window了
console.log(this) // window
})
全局函数的this的指向
- 一般来说 this的指向 判断依据 谁调用了 函数 this 指向谁
- 其实 当定义了全局的函数的时候,默认是挂载到了window 全局变量上
- 全局函数 其实就是 window的一个属性而已 只不过 平时写代码的时候 可以省略这个window
- 当定义全局的函数的时候 本质是给window添加了一个 属性(函数类型)
- 当调用这个函数的时候 如:person() 本质 window.person() window 可以被省略而已
function person() {
console.log("ABC")
console.log(this) // this指向window
}
person() // 调用了this,this指向了window
window.sonPerson = function () {
console.log('ABC')
console.log(this) // this指向window
}
window.sonPerson() // 调用了this,this指向了window
const obj = {
username: "悟空",
say() {
console.log(this) // this指向obj
}
}
obj.say()
构造函数
-
构造函数 本质 其实也是一个函数
-
作用 用来创建对象
-
以前见过构造函数
-
只要它被new 它就是构造函数
- 如:Person 构造函数 首字母是大写(规范)
- 如:per 被new出来的对象是 实例
// Person 就是一个构造函数 被new
function Person() {}
// per 就是实例
const per = new Person()
构造函数的this
- 每一个构造函数中 都存在 一个 魔鬼 this
- 构造函数 默认情况下 就是返回了 this
- this 等于你所new出来的实例
// 只要给构造函数中的this 添加 属性或者方法
// 那么 实例 自然拥有对应的属性和方法
function Person() {
this.username = '悟空' // Person增加了一个属性
this.add = function () {} // Person增加了一个方法
this.clear = function () {}
}
// 注意:
console.log(Person.username) // 无法拿到Person的元素
console.log(Person.username = '沙和尚') // 控制台显示 沙和尚,但实际Person里面的username还是 悟空
console.log(Person) // username属性还是 悟空
console.log(this.username) // 无法拿到Person的元素
const per = new Person()
per.nickname = '八戒' // 直接添加,per有、per2没有
const per2 = new Person()
console.log(per)
console.log(per2)
console.log(per2.username) // 可以拿到Person的元素
构造函数的弊端
function Person() {
this.say = function () {}
}
// p1 和 p2 没有共享一个方法(占更多的内存)
const p1 = new Person()
const p2 = new Person()
console.log(p1 === p2) // false 两个对象的比较 内存地址
console.log(p1.say === p2.say) // false 两个函数 存放在不同地址中
// s1 和 s2 共享一个方法
const s1 = new Set()
const s2 = new Set()
console.log(s1 === s2) // false 两个对象的比较 内存地址
console.log(s1.say === s2.say) // true 两个函数 存放在相同地址中
构造函数-方法指向同一个(这个全局(方法)变量不推荐,后面的 构造函数+原型 更好 )
- 值类型 是存在 栈空间 适合存放 固定大小的数据
- 引用类型(对象、数组、函数) 存在 堆空间 适合存放大小不固定的数据
- new了两个对象的时候, 两个对象的内存地址 不相等
- 希望 两个对象的方法 内存地址是相等
- 在构造函数中 当我们定义方法的时候 一般都不会只在 构造函数中写死
- 让 方法 都指向外部 单独声明的方法 多个实例去共享方法
- 缺点:污染全局变量,意思是有其他人可能使用这些属性、方法,就会造成覆盖
// newSay 全局(方法)变量
function newSay() {
console.log('外部单独的newSay方法')
}
function Person() {
this.say = newSay
}
const p1 = new Person()
const p2 = new Person()
console.log(p1 === p2) // false
console.log(p1.say === p2.say) // true
构造函数基本使用(这个全局(方法)变量不推荐,后面的 构造函数+原型 更好 )
- 构造函数的属性
- 函数类型的属性(指方法):一般都是写在外部
- 非函数类型的属性(指属性):一般是写在内部
// say 全局(方法)变量
function say() {
console.log('这个是Person的一个方法', this.name)
}
// fly 全局(方法)变量
function fly() {
console.log(this.name, '要起飞')
}
function Person(name, height) {
this.name = name
this.height = height
this.say = say
this.fly = fly
}
const p1 = new Person('八戒', 150)
p1.say()
p1.fly()
console.log(p1)
console.log(p1.name, p1.height)
构造函数+原型(es6比es5(构造函数+原型)更好)
- 原型 本质 是一个对象
- 当创建一个构造函数的时候 原型 就被创建了
- 如果我们在原型对象上 增加一个属性或者方法 那么 实例 拥有所增加的属性或者方法
- 原型 就是 DNA;构造函数 就是 父亲;实例 就是 孩子
function Person() {
this.name = '悟空'
this.b = function () {}
}
console.log(Person.prototype)
// 访问原型
Person.prototype.a = function () {
console.log('这个是a方法')
}
const p1 = new Person()
console.log(p1)
p1.a()
原型实现方法的继承-prototype
function Person() {}
Person.prototype.say = function () {
console.log('father', '父亲定义的say方法')
}
const father = new Person()
function SonPerson() {}
// 让孩子SonPerson去复用父亲Person的方法 即继承父亲的方法
SonPerson.prototype.say = Person.prototype.say
// 添加其他方法
SonPerson.prototype.jump = function () {
console.log('son', '孩子自己的方法')
}
const son = new SonPerson()
Person.prototype.say() // ok:Person的原型(prototype)的方法say()
SonPerson.prototype.say() // ok:SonPerson的原型(prototype)的方法say()
// son.say = father.say // 等同于 SonPerson.prototype.say = Person.prototype.say 前提条件:Person和SonPerson都被 new 后
son.say()
son.jump()
原型实现属性的继承-call
function Person(name, color, height, weight) {
this.name = name
this.color = color
this.height = height
this.weight = weight
}
function SonPerson(name, color, height, weight, nickname, gender) {
// Person父亲中也有 注意:this不能忘写
Person.call(this, name, color, height, weight) // 属性的继承
// SonPerson孩子
this.nickname = nickname
this.gender = gender
}
const son = new SonPerson('悟空', '黄色', 150, 250, '弼马温', '公')
console.log(son)
console.log(son.name)
原型快速继承
// 作用:快速将父亲的原型复制到儿子上
function Person() {}
Person.prototype.add = function () {}
Person.prototype.remove = function () {}
Person.prototype.clear = function () {}
function SonPerson() {}
// SonPerson.prototype.add= Person.prototype.add
// SonPerson.prototype.remove= Person.prototype.remove
// SonPerson.prototype.clear= Person.prototype.clear
// 让儿子的原型 指向 父亲的原型
// SonPerson.prototype = Person.prototype // error:堆地址联系在一起,修改一个,另一个也被修改
// 对象复制(剩余运算符)
SonPerson.prototype = {
...Person.prototype
}
SonPerson.prototype.cunstructor = SonPerson // 添加方法到SonPerson的原型
SonPerson.prototype.del = function () {} // 添加方法到SonPerson的原型
const son = new SonPerson()
console.log(son)
const per = new Person()
console.log(per)
es6类的基本使用
- 构造函数 来创建对象
- 对象 两种属性信息
- 非函数类型的属性(指属性)
- 函数类型的属性(指方法)
// es6 新 简洁 class 面向对象
class Person {
// this不用写,一写就报错
name = '悟空'
color = '红色'
say() {
console.log(this.name, this.color)
}
fly() {
console.log(this.name, '起飞')
}
}
// es5 原型链方式实现 面向对象
function Person2() {
this.name = "悟空"
this.color = "紫色"
}
Person2.prototype.say = function () {
console.log(this.name, this.color)
}
Person2.prototype.fly = function () {
console.log(this.name, '降落')
}
const p1 = new Person()
p1.say() // 悟空 红色
p1.fly() // 悟空 起飞
const p2 = new Person2()
p2.say() // 悟空 紫色
p2.fly() // 悟空 降落
es6类的(动态)基本使用
// 使用 class 来定义属性的时候
// 1.如果 这个属性 写死了
// class Person {
// name="悟空"
// color="黄色"
// }
// 2.如果 属性 可以由外部传入 必须使用构造器
// constructor(name,color){
// this.name=name
// this.color=color
// }
class Person {
// 构造器
constructor(name, color) {
// 当 Person被new的时候 这个构造器 就会被调用
console.log('Person被new了')
console.log(name, color)
// this 还是指向 实例 注意:this要写
this.name = name
this.color = color
}
say() {
console.log(this.name, this.color)
}
}
const p1 = new Person('悟空', '红色')
p1.say()
console.log(p1)
es6类实现属性和方法的继承
// // es5类 去实现继承
// function Person() {
// this.name = "父亲"
// }
// Person.prototype.say = function () {
// console.log(this.name, "父亲的say方法")
// }
// function SonPerson(name) {
// Person.call(this, name) // 属性的继承
// }
// SonPerson.prototype.say = Person.prototype.say // 方法的继承
// const son = new SonPerson
// console.log(son.name)
// son.say()
// es6类 去实现继承
class Person {
name = '父亲'
say() {
console.log(this.name, '父亲的say方法')
}
}
// 直接实现了 继承父亲的属性和方法
class SonPerson extends Person {}
// 孩子实例 拥有 父亲 name属性和say方法
const son = new SonPerson()
console.log(son)
console.log(son.name)
son.say()
es6类实现(动态)属性和方法的继承
// 对于孩子来说
// 1.如果写了 extends 而且 还写 constructor
// 那么在 constructor 必须要调用super 固定语法
// 2.之前只写 extends 不用写super 因为没有写 constructor
class Person {
constructor(name, color) {
this.name = name
this.color = color
}
say() {
console.log(this.name, this.color)
}
}
class SonPerson extends Person {
// 默认的代码
constructor(name, color, height, weight) {
super(name, color) // 调用父亲的构造器 给孩子 添加属性
this.height = height
this.weight = weight
}
}
const son = new SonPerson('悟空', '紫色', 100, 200)
console.log(son)
console.log(son.height)
son.say()
检测数据类型(检测实例被这个构造函数 new)
// 检测 基本的数据类型 typeof
// 检测 引用数据类类型 intanceof
// 检测 这个实例是不是被某个构造函数 new 出来
console.log(typeof '') // string
console.log(typeof 1) // number
function Person() {}
class SuperPerson {}
const p1 = new Person()
const s1 = new SuperPerson()
console.log(p1 instanceof Person) // true
console.log(s1 instanceof SuperPerson) // true
console.log(p1 instanceof SuperPerson) // false
对象中的this和call
- this 范围很广 主要在 对象中来学习
- this 指向 对象本身 可以通过 call 方法来修改
- call 在调用方法的时候,可以修改 this 的指向
- call 在调用方法的时候,可以传递参数
const obj = {
height: 100,
username: '悟空',
say(a) {
console.log(this, a) // this指向 obj
},
}
const newObj = {
username: '八戒'
}
obj.say()
console.log(obj)
obj.say.call() // call修改this的指向,变成window
console.log('----------------------------------')
obj.say(123)
obj.say.call(this, 123) // this指向 window
obj.say.call(newObj, 123) // this指向 newObj
对象添加属性的方式
// 对象添加属性的普通方式
const newObj = {
a: 1,
b: 1,
}
newObj.c = 2
console.log(newObj)
// 在对象中 this = 对象本身 this = 我自己
// 当成普通的对象
const obj = {
a: 1,
e: 1,
add() {
// 给对象添加属性 封装到一个普通的函数中
obj.a = 3 // 会覆盖原来的a属性
obj.b = 3
// 等同上面 obj修改成this
this.c = 4
this.d = 4
}
}
console.log(obj) // add方法没使用时,只有a、e、add()
obj.add() // 让方法帮我们添加属性
console.log(obj)
动态给对象添加属性-call
// 当成普通的函数
function person(name, color, height, weight) {
this.name = name
this.color = color
this.height = height
this.weight = weight
}
// 定义一个空的对象
const obj = {}
person.call(obj, '悟空', '红色', 150, 250) // obj 借用了person方法 来给自己的属性赋值
// 1.调用了 Person 方法
// 2.方法内部 给 this赋值 this 等于 obj
// 3 this.name=悟空 => obj.name = 悟空 // 赋值动作
// 3 this.color=黄色 => obj.color = 黄色 // 赋值动作
// 3 this.height=150 => obj.height = 150 // 赋值动作
// 3 this.weight=250 => obj.weight = 250 // 赋值动作
console.log(obj) // 拥有了 Person 函数中一些属性
修改this指向:bind、call、apply
- call 和 apply 会在调用原函数的同时也修改this的指向
- bind会修改this指向 但是 不会直接调用原函数 而是会返回一个 修改了this指向 的新函数
- call 和 apply 区别 传递参数的方式不同而已
- 如:obj.say.call(newObj,1,2) // 数字,字符串
- 如:obj.say.apply(newObj,[1,2]) // 数组
- 默认情况下 this的指向 谁调用了this,this 指向谁
const obj = {
username: '悟空',
say() {
console.log(this.username, 'obj中的say方法 ')
},
say1(a, b) {
console.log(a, b)
},
}
const newObj = {
username: '八戒',
}
obj.say.call(newObj)
obj.say.apply(newObj)
const fn = obj.say.bind(newObj) // 返回一个新的函数 新函数内部 修改this指向的功能
fn()
console.log('---------------------------')
obj.say1.call(newObj, 1, 2)
obj.say1.apply(newObj, [1, 2]) // 参数必须要以数组的形式来传递
const fn1 = obj.say.bind(newObj)
fn1(1, 2)