前言
JavaScript属于多范式编程,今天我们来探讨下JavaScript中的面向对象编程。本篇文章包含对象的属性描述符、原型、构造函数、原型链。
一、Object.defineProperty(obj,属性,属性描述符)
当我们需要对一个属性进行更加精准的 *** 作时,可以使用属性描述符。或许说到属性描述符大家会有点陌生,因为在实际的开发中非常少使用到,但有看过vue2源码的小伙伴们,应该是知道vue2的响应式原理就是通过属性描述符来实现的。
话不多说直接上干货!属性描述符分为数据属性描述符和存取属性描述符。
1.数据属性描述符configurable:是否可以通过delete删除属性,是否可以修改属性描述符类型,是否可以修改属性特性
enumerable:是否可以通过遍历出该属性(for…in…、Object.keys()等)
writable: 是否可以修改属性值
value:属性值
var obj = {name:"susan",age:18}
Object.definProperty(obj,"address",{
configurable:true,
enumerable:true,
writable:true,
value:"北京市"
})
2.存取属性描述符
configurable:是否可以通过delete删除属性,是否可以修改属性描述符类型,是否可以修改属性特性
enumerable:是否可以通过遍历出该属性(for…in…、Object.keys()等)
get: 获取属性
set:设置属性
var obj = {name:"susan",age:18,_address="北京市"}
Object.definProperty(obj,"address",{
configurable:true,
enumerable:true,
get:function(){
return this._address
},
set:function(value){
this._address = value
}
})
用途:1.对象中设置私有属性,不希望直接被外界获取或修改
2.用于截获对象中一个属性设置和被访问的过程(vue2的响应式原理)
每一个对象都有一个特殊的内置属性[[prototype]],该属性本质是一个对象。
我们可以通过__proto__在浏览器中去查看下这个属性,但在开发中不要这样去获取到原型。ES5之后可以通过Object.getPrototypeOf()获取到原型。
但由于直接去获取一个对象的原型,会发现打印出来的是空对象,那是因为该对象上的很多属性的属性描述符的enumerable:false。那我们直接简单粗暴看下这个原型所有的属性描述符。
代码如下(示例):
var obj = {name:"susan",age:18}
console.log(Object.getOwnPropertyDescriptors(obj.__proto__))
打印结果:
{"constructor":{"writable":true,"enumerable":false,"configurable":true},"__defineGetter__":{"writable":true,"enumerable":false,"configurable":true},"__defineSetter__":{"writable":true,"enumerable":false,"configurable":true},"hasOwnProperty":{"writable":true,"enumerable":false,"configurable":true},"__lookupGetter__":{"writable":true,"enumerable":false,"configurable":true},"__lookupSetter__":{"writable":true,"enumerable":false,"configurable":true},"isPrototypeOf":{"writable":true,"enumerable":false,"configurable":true},"propertyIsEnumerable":{"writable":true,"enumerable":false,"configurable":true},"toString":{"writable":true,"enumerable":false,"configurable":true},"valueOf":{"writable":true,"enumerable":false,"configurable":true},"__proto__":{"enumerable":false,"configurable":true},"toLocaleString":{"writable":true,"enumerable":false,"configurable":true}}
你一定会说给我看这个干啥?这玩意到底有啥用?
我们需要知道的是。当我们从一个对象中获取一个属性,会触发[[get]] *** 作。
1.当在该对象中查询某个属性,如果在该对象中可以找到就直接从该对象中获取
2.如果在该对象中没有找到,就会沿着该对象的原型链去找。
不信,咱们立马校验一下,我说的是否正确?
var obj = {name:"susan",age:18}
obj.__proto__ = {address:"北京市"}
console.log(obj.address)
打印结果:
> 北京市
在obj对象中并没有address属性,但是打印的结果是obj原型上的address属性。
2.new *** 作符既然知道了原型的概念,那我们来说下当执行new *** 作符的时候,都做了些什么 *** 作?
1.在内存中创建一个空对象
2.这个对象内部的__proto__属性被赋值为该构造函数的proptype属性(函数是一个对象,函数具有__proto(隐式原型)和函数独具的prototype(显示原型))
为了验证这一步的 *** 作,直接上代码:
var Person = function(){
this.name = "susan",
this.age = 18
}
const person1 = new Person()
console.log(person1.__proto__ === Person.prototype)
打印结果:
> true
3.构造函数内部的this指向新创建出来的对象
4.执行函数中的代码
5.如果构造函数没有返回非空对象,则返回创建出来的新对象。
咱们带着几个疑问,来看下原型链。
1.既然原型是个对象,对象中又有内置的__proto__,那是不是可以obj.proto.proto.__proto__无限下去?
2.那如果不会无限查找下去,那顶层的原型到底是什么东西?
我们先来简单分析一下:对象可不可以理解成是通过Object构造函数创建出来的,那Object是所有对象的父类。当使用new *** 作符创建出来的对象隐式原型被赋值为Object构造函数的prototype属性。那prototype属性本身就是一个对象,含有__proto__属性,那我们直接看下Object的prototype属性中__proto__到底指向的是个什么东西?(这里有点绕,多看几遍理解一下)
接下来直接上代码验证下,咱们分析的对不对
var obj = {name:"susan",age:18}
console.log(obj.__proto__ === Object.prototype)
打印结果:
true
var obj = {name:"susan",age:18}
console.log(Object.prototype.__proto__)
打印结果:
> null
通过打印的结果可以看到,最后Object.prototype.proto = null,由此可以看出其实原型链并不是无限长的,当它查询到最后的原型指向null时就停止查询了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)