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关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!