原型回顾
上次讲原型和原型链,对比Java中的继承和重写其实不难理解,原型是function对象的一个属性,它定义了构造函数构造出的对象的公共祖先,由构造函数产生的对象,可以继承对应原型的属性和方法,原型也是对象。
原型链就是把原型串起来,在原型上面再加一个原型,再加一个原型,使用__proto__
连接各个原型。
还学到了原型链的增删改查,一般情况下不能通过后代改父代,但要注意可以修改的情况。见前文
好友call/apply
的作用和不同点,作用是改变this指向,在起夜开发中可用于组装部件,apply
的区别在于把参数写成一个数组传进去,而不是像call那样一个一个传进去
继承模式
extend走过许多发展史,
- 原型链只是继承的一种方法,虽然简单,但是它也会继承许多没用的属性
- 于是有了call/apply那种借用构造函数的方式,虽然它不太像继承,缺点是它不能继承借用构造函数的原型,且每次构造函数都要走一个函数,从视觉上减少了代码量,但是运行成本并没有减少。
- 然后到了现在使用很多的共享原型的模式,即多个构造函数使用一个原型,直接赋值即可,比如
Target.prototype = Origin.prototype;
- 第三种我们会发现
Target.prototype
和Origin.prototype
完全指向了一个空间,修改其中一个另一个势必也会跟着修改,这或许不是我们想要的,圣杯模式恰好解决了这种问题,此时改动Son的prototype并不会改变Father的,这就是圣杯模式
在雅虎YUI库中有关继承的封装好的函数,是像下面这样写的,有什么好处呢?
好处是使用了闭包和立即执行函数,私有化变量,不污染执行环境,对比前一种方法,F根本只起了一个中间层的作用
命名空间
namespace就是对象,大型项目有多人开发,可能存在定义了同名变量,那么就把变量放到命名空间统一管理,防止污染全局,适用于模块化开发,同时此处的模块化开发也是闭包的用处之一
现在流行的解决方式是webpack
对象枚举
附加
对象属性访问
- 方括号运算符:方括号运算符内可以是表达式,表达式必须返回字符串或一个可以转换为字符串的值(因为有些代码就是使用字符串来作为对象的键)
- 点运算符:右侧必须是一个标识符,只能表示键是一个合法的标识符且不是一个保留字的情况
若在属性中没有找到相关的键,就会返回undefined
其中属性分为自有属性(直接在对象中定义的属性) 和继承属性(在当前对象的原型对象中定义的属性)
以前笔记提到过,在前后端交换数据时,为了避免直接报错,可以使用或运算符或者与运算符
属性检测与遍历
属性检测
JS有三种方法检测某个属性是否在特定的对象中
-
in
:检测属性是否为特定对象的自有属性或继承属性 例如'name' in person
输出true -
hasOwnProperty()
:确定JS对象是否具有指定的自有属性。例如person.hasOwnProperty("name")
输出true -
propertyIsEnumerable()
:该propertyIsEnumerable()
方法返回一个布尔值,该布尔值指示指定的属性是否可枚举并且是对象自己的属性。例如person.propertyIsEnumerable("name")
输出true
什么是属性的可枚举性?
属性的特性之一,用来描述属性是否可以用一般的遍历操作获取到值,可枚举属性是其内部可枚举标志设置为true的那些属性,这是通过简单赋值或通过属性初始化器创建的属性的默认值(通过Object.defineProperty
定义的属性,此类默认可枚举为false)。
属性遍历
JS提供三种方法遍历对象中的属性
-
for...in
:遍历由字符串键控的对象的所有可枚举属性(忽略由Symbol键控的那些),包括继承的可
-
Object.keys(person)
:返回一个数组,数组中的元素是对象中的可枚举的自有属性的名称,输出(3) ["name", "isMale", "age"]
-
Object.getOwnPropertyNames(person)
:返回一数组,数组中的元素是对象中所有自有属性的名称
存取器属性
只能get方法,即只能读,只有set方法,即只能写,读取只写属性返回undefined
由这两个方法定义的属性称为存取器属性,而有其他简单的值定义的属性称为数据属性
存取器详情见此文
属性的特性
下面的文字源于shangCW的文章
除了在前文提到的可枚举性之外,对象的属性还有其它的特性,比如说可枚举性、可配置性等等
如果我们把数据属性的值看作是一个特性,那么数据属性总有具有四个特性,分别是
- 值(value):数据属性的值,默认为 undefined
- 可写性(writable):是否可以修改属性的值,默认为 true
- 可枚举性(enumerable):是否可以通过 for/in 循环获取到值,默认为 true
- 可配置性(configurable):是否可以删除和修改属性,默认为 true
如果我们把存取器属性的 getter 和 setter 方法也看作是特性,那么存取器属性同样具有四个特性,分别是
- 读取(getter):在读取属性时调用的函数,默认为 undefined
- 设置(setter):在设置属性时调用的函数,默认为 undefined
- 可枚举性(enumerable):是否可以通过 for/in 循环获取到值,默认为 true
- 可配置性(configurable):是否可以删除和修改属性,默认为 true
至此,我们对属性又有一个不同的理解:对象的属性是由一个名字(键)和四个特性组成的
- 可使用
Object.getOwnPropertyDescriptor(square, "x")
来获取获取自有属性的描述符,输出如下
- 可使用使用 Object.defineProperty() 方法设置属性的特性
注意最后的输出结果,因为get area()
只读,而set area()
只写,x又被我设置了不可枚举,所以只能输出area