JS高级知识总结
执行上下文,作用域,this指向,闭包,箭头函数相关内容查看文章[深入理解js - AzirKxs的博客 | 分享与记录](http://8.130.40.222/cn/base05- 深入理解js/)
严格模式
简介
开启严格模式会以更加严格的方式对代码进行检测和执行,使代码脱离“懒散(sloppy)“模式
严格模式有如下限制
严格模式的限制
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。
注意,get、set描述符与vaule、writable描述符不共存。
获取对象属性描述符
getOwnPropertyDescriptor
getOwnPropertyDescriptors
阻止对象扩展属性
Object.preventExtensions
密封对象
Object.seal() 不允许配置和删除属性
实际上就是调用preventExtensions并且修改configurable:false
冻结对象
Object.freeze()不能进行写入
实际上是调用seal并且修改writable:false
原型、原型链
对象的原型
每个对象都有自己的原型
获取原型对象
obj.__ prototype __(隐式原型,由浏览器提供,有个别浏览器访问不到)
Object.getPrototypeOf()
原型的作用
当我们通过[[get]]方式获取一个属性对应的value时,它会优先在自己的对象中查找,如果找到直接返回,如果没有找到则会去原型对象中查找
函数的原型
函数也可以看做是一个对象,因此也有proto属性
获取函数的原型对象
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()
constructor
每个原型上都会添加一个属性叫constructor,这个属性指向当前函数对象
原型链
原型链是由原型所构成的,当我们从一个对象上获取属性的时候,如果当前对象中没有就会在它的原型链上获取
1 2 3 4 let a = new foo();console .log(a.__proto__) console .log(a.__proto__.__proto__) console .log(a.__proto__.__proto__.__proto__)
原型链继承
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 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' )
问题:
在原型链上创建了属性(一般来说,这不是一种好的实践)
私自篡改__proto__
1 2 console .log(me instanceof Student) console .log(me.__proto__ === Student.prototype)
盗用构造函数
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)
优点:
父类的方法可以复用
可以在Child构造函数中向Parent构造函数中传参
父类构造函数中的引用属性不会被共享
缺点:
父类构造函数调用了两次
借用的构造函数中的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)
图示
优点:
只调用一次父类构造函数
Child可以向Parent传参
父类方法可以复用
父类的引用属性不会被共享
Function和Object的关系
Object是Function的父类
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()
那是因为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()
对象的方法补充
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 { 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 { 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) p.name = 'kxs' console .log(p.name)
静态方法
类方法也叫做静态方法,可以通过类直接调用(不能通过实例对象调用)
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~' ) } 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) p.name = 'kxs' console .log(p.name) 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关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!