js

JS高级知识总结01

严格模式,原型/原型链,继承,ES6类新特性

Posted by AzirKxs on 2022-10-28
Estimated Reading Time 11 Minutes
Words 2.7k In Total
Viewed Times

JS高级知识总结

执行上下文,作用域,this指向,闭包,箭头函数相关内容查看文章[深入理解js - AzirKxs的博客 | 分享与记录](http://8.130.40.222/cn/base05- 深入理解js/)

严格模式

简介

开启严格模式会以更加严格的方式对代码进行检测和执行,使代码脱离“懒散(sloppy)“模式

严格模式有如下限制

  • 通过抛出错误来消除一些原有的静默错误

  • 使得JS引擎在执行代码可以进行更多的优化

  • 禁用了在ECMAScript未来版本中可能定义的一些语法

严格模式的限制

1.无法意外的创建全局变量

2.引起静默(不报错也没有任何效果)错误抛出异常 ,例如更改只读属性,删除不可删除的属性时报错

3.不允许函数参数有相同的名称

4.不允许0的八进制语法

5.不允许使用with

6.eval不能创建外层变量

7.this不会默认转成对象类型(非严格模式下undefined和null都会转化为window)

属性描述符

可以通过Object.defineProperty对一个对象的属性的描述符进行配置

数据属性描述符

  • value该属性对应的值,默认undefined
  • configurable该属性描述符是否可被改变、是否可被删除,默认为false
  • enumerable该属性是否可被枚举,默认为false
  • writable该属性是否可以被写入新的值,默认为false

存取属性描述符

  • 存储属性描述符(Accessor Properties)
    • get当访问该属性时,会调用此函数,默认为undefined
    • set当属性值被修改时,会调用此函数,默认为undefined

注意,getset描述符与vaulewritable描述符不共存。

获取对象属性描述符

getOwnPropertyDescriptor

getOwnPropertyDescriptors

阻止对象扩展属性

Object.preventExtensions

密封对象

Object.seal() 不允许配置和删除属性

实际上就是调用preventExtensions并且修改configurable:false

冻结对象

Object.freeze()不能进行写入

实际上是调用seal并且修改writable:false

原型、原型链

对象的原型

每个对象都有自己的原型

获取原型对象

obj.__ prototype __(隐式原型,由浏览器提供,有个别浏览器访问不到)

Object.getPrototypeOf()

原型的作用

当我们通过[[get]]方式获取一个属性对应的value时,它会优先在自己的对象中查找,如果找到直接返回,如果没有找到则会去原型对象中查找

函数的原型

函数也可以看做是一个对象,因此也有proto属性

获取函数的原型对象

1
foo.prototype //显示原型

new操作中的原型

new操作的步骤如下:

1.创建一个空对象

2.将对象赋值给this

3.对象内部的[[prototype]]属性会被赋值为该构造函数的[[prototype]]属性(将函数的显式原型赋值给对象的隐式原型)

4.执行函数中的代码体

5.将这个对象默认返回

当多个对象拥有共同的值的时候,我们可以将它放到构造函数的原型上, 这样做的好处是节省空间

1
2
3
4
5
6
7
8
9
10
11
function student(name,age){
this.name = name
this.age = age
}

student.prototype.eat = function(){
console.log(`${this.name}正在吃饭`)
}

let kxs = new student('kxs',22)
kxs.eat() //kxs正在吃饭

constructor

每个原型上都会添加一个属性叫constructor,这个属性指向当前函数对象

原型链

原型链是由原型所构成的,当我们从一个对象上获取属性的时候,如果当前对象中没有就会在它的原型链上获取

1
2
3
4
let a = new foo();
console.log(a.__proto__) //foo的显示原型foo.prototype
console.log(a.__proto__.__proto__) //Object
console.log(a.__proto__.__proto__.__proto__) //null

原型链继承

Object是所有类的父类,所有类都是Object类的子类

原型链继承的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Person(name,age){
this.name = name;
this.age = age;
this.info ={
msg:'该信息会被子类共享'
}
}

let p = new Person('azir',22)

function Student(name,age,height,address){
this.name = name
this.age = age
this.height = height
this.address = address
}

Student.prototype = p


let me = new Student('azir',22,175,'china')
let you = new Student('kxs',22,185,'china')
me.info.msg = '更改信息'
console.log(me)
console.log(you)
console.log(me.info.msg) //更改信息
console.log(you.info.msg) //更改信息

优点:

父类方法可以复用

缺点:

  1. 在原型上创建了属性,此时,多个子类的实例将共享同一个父类的属性
  2. 子类型实例不能给父类型构造函数传参
  3. 父类引用属性将被子类共享
改进
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Person(name,age){
this.name = name;
this.age = age;
this.info ={
msg:'该信息会被子类共享'
}
}

function Student(name,age,height,address){
this.height = height
this.address = address
const prototype = new Person(name,age)
this.__proto__ = prototype
}


let me = new Student('azir',22,175,'china')
let you = new Student('kxs',22,185,'china')

问题:

  1. 在原型链上创建了属性(一般来说,这不是一种好的实践)
  2. 私自篡改__proto__
1
2
console.log(me instanceof Student) //false
console.log(me.__proto__ === Student.prototype) //false

盗用构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name,age){
this.name = name;
this.age = age;
}


function Student(name,age,height,address){
Person.call(this,name,age)
this.height = height
this.address = address
}


let me = new Student('azir',22,175,'china')
console.log(me)

优点:

  • 可以在子类构造函数中向父类传递参数

  • 父类的引用属性不会被共享

缺点:

  • 子类不能访问父类原型上定义的方法(即不能访问Parent.prototype上定义的方法),因此所有方法属性都写在构造函数中,每次创建实例都会初始化

组合继承

组合继承结合了原型链继承和盗用构造函数继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(name,age){
this.name = name;
this.age = age;
}


function Student(name,age,height,address){
Person.call(this,name,age)
this.height = height
this.address = address
}

Student.prototype = new Person()

let me = new Student('azir',22,175,'china')
console.log(me)

优点:

  1. 父类的方法可以复用
  2. 可以在Child构造函数中向Parent构造函数中传参
  3. 父类构造函数中的引用属性不会被共享

缺点:

父类构造函数调用了两次

借用的构造函数中的name和age属性没有存在的必要

寄生组合式继承

寄生组合式继承不在依赖父类的实例对象来实现继承,而依赖一个新的构造函数,使该构造函数的原型指向父类的原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//使该构造函数的原型指向父类
function creatObject(o){
function f() {};
f.prototype = o
return new f()
}

//子类原型指向新创建的对象
function inherit(subType,superType){
subType.prototype = creatObject(superType.prototype)
Object.defineProperty(subType.prototype,'constructior',{
enurmable:false,
configurable:true,
writable:true,
value:subType
})
}

function Person(name,age){
this.name = name;
this.age = age;
}


function Student(name,age,height,address){
Person.call(this,name,age)
this.height = height
this.address = address
}

inherit(Student,Person);

let me = new Student('azir',22,175,'china')
console.log(me)

图示

组合寄生式继承

优点:

  1. 只调用一次父类构造函数
  2. Child可以向Parent传参
  3. 父类方法可以复用
  4. 父类的引用属性不会被共享

Function和Object的关系

  • Object是Function的父类
  • Function是Object的构造函数

用一张图描述:

Function和Object的关系

最后的null是为了避免死循环而设计的

类方法

添加到原型上的方法不能够通过类名直接调用

1
2
3
4
5
6
7
8
9
10
function foo(name,age){
this.name = name
this.age = age
}

foo.prototype.running = function(){
console.log('running~')
}

foo.running() //error foo.running is not a function

那是因为foo本身没有running属性,因此会去隐式原型上找,而foo的隐式原型式Function的显式原型,因此找不到,在原型上添加的方法也可以被称之为实例方法

正确添加类方法的方式是作为构造函数对象本身的属性添加

1
2
3
4
5
6
7
8
9
10
function foo(name,age){
this.name = name
this.age = age
}

foo.running = function(){
console.log('running~')
}

foo.running() //running~

对象的方法补充

hasOwnProperty() 判断对象自己是否有某个属性

in() 判断原型上是否有某个属性/for in 遍历原型上的属性(不包括不可枚举的属性)

instanceof () 检测某个对象是否是某个构造函数的实例(检测构造函数的prototype,是否出现在了实例对象的原型链上)

isPrototypeOf() 用于检测某个对象,是否出现在了某个实例对象的原型链上

ES6中的类

使用class定义类

ES6中可以直接使用class关键字来定义一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
//new对象时自动调用constructor方法
constructor(name,age){
this.name = name
this.age = age
}

//实例方法
running(){
console.log('running~')
}
}

let p = new Person('azir',22)

使用class定义的类与用构造函数定义的类没有什么异同,唯一的区别是使用class定义的类不能作为一个函数去调用

访问器get与set

当读取对象的属性时回调用get方法,更改属性时会调用set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person {
//new对象时自动调用constructor方法
constructor(name,age){
this._name = name
this._age = age
}

//实例方法
running(){
console.log('running~')
}

get name(){
return this._name
}

set name(value){
this._name = value
}
}

let p = new Person('azir',22)
console.log(p.name) //azir
p.name = 'kxs'
console.log(p.name) //kxs

静态方法

类方法也叫做静态方法,可以通过类直接调用(不能通过实例对象调用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Person {
//new对象时自动调用constructor方法
constructor(name,age){
this._name = name
this._age = age
}

//实例方法
running(){
console.log('running~')
}

get name(){
return this._name
}

set name(value){
this._name = value
}

static test(){
console.log('这是一个静态方法')
}
}

let p = new Person('azir',22)
console.log(p.name) //azir
p.name = 'kxs'
console.log(p.name) //kxs
Person.test()//这是一个类方法

继承

ES6引入了extends和super关键字来实现继承

extends和super实现继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Person {
constructor(name,age){
this._name = name
this._age = age
}

running(){
console.log('running~')
}

}

class Student extends Person{
constructor(name,age,height,id){
super(name,age)
this._height = height
this._id = id
}

get info(){
return `姓名:${this._name}
年龄:${this._age}
身高:${this._height}
学号:${this._id}`
}
}

let p = new Student('azirkxs',18,175,'2019006152')
console.log(p.info)
p.running()

super关键字

super可以用于继承,super必须放在构造函数的第一行使用

super也可以用来调用父类的方法

mixin混入实现多继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function mixinAnimal(baseClass){
return class extends baseClass{
running(){
console.log('running~')
}
}
}

function mixinFlying(baseClass){
return class extends baseClass{
flying(){
console.log('flying~')
}
}
}

class Bird{
eating(){
console.log('eating~')
}
}

class NewBird extends mixinFlying(mixinAnimal(Bird)){

}

console.log(new NewBird())

如果这篇文章对你有帮助,可以bilibili关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!