我们先从盘古开天辟地时捋一捋对象:
从宏观内容来讲,javascript是一个属性的集合,包括值,函数,而整个集合也可以类比为一个对象。
js = {
a的变量名: a的值,
...
函数b: function () {}
...
}
注: 这里的js指的是javascript对象(ECMAscript对象),而不是DOM和BOM对象。
紧接着我们听到一个“传说”:‘javascript中一切都是对象’ ,或者说是有这么一个说法,但到底说的正确还是不正确,我们还需要自己追根溯源,挖掘一下:
从js来讲,变量和方法之间联系很紧密,变量可以通过原生js对象的构造方法创建,而方法又可以通过创建的变量(“对象”)调用,注意这里的对象是带了引号的,这就说明它不是真正意义上的对象,下文会说明原由。
js中一共由6中基本数据类型:number,string,boolean,null,undefined,symbol(es6新增)
从属性值方面讲:一个变量可以通过两种方式创建:
1. 由于js为弱类型语言,所以可直接赋值创建
var a = 1; console.log(typeof a) // number
2. 由原生对象通过构造函数创建
var b = new Number(1); console.log(typeof b) // object
为确定它们都是数字,我们可以判断一下:
console.log(a == b) // true
由此可知,a和b只是创建方式不同,而a也确实是基本数据类型,但是,基本数据类型又能否向对象一样进行添加属性的操作呢,答案是可以的:
var a = 1; a.prop = 2; // 不会报错 console.log(a) // 1 console.log(a.prop) // undefined console.log(a.toString()) // 1 (字符串)
那么变量a的数据类型为基本数据类型,却又可以进行对象赋值操作(这里只能够赋值,但赋值没有成功),还可以调用对象的方法。
回想一下,我们经常是通过最简单的方法定义变量(基本类型可以通过直接赋值,引用类型可以通过字面量),而这些方法是为了简化操作。当基本数据类型触发对象行为时,系统会为基本数据类型创建一个包装对象,当对象行为完成后,该包装对象即被销毁。
现在回来说,js中的一切是不是对象,主要在于对基本数据类型的操作,常规操作时,它只是基本数据类型,特殊使用时,基本类型又会成为包装对象。
由此,可以总结出,js的一切是不是对象,完全在于你的代码习惯,或者代码功能,确实没有一个确定的答案,主要在于你个人的理解。就像人妖到底是人还是妖,完全在于你在生活中怎么去看待。
源头说的差不多了,下面我们来具体说明:
在js中所有通过对象创建的对象,都有一个原型。这些对象都有一个通过原型链接的父级,而这些链接起对象的原型就是原型链。
在js中,除了null(空对象)和Object.prototype之外,都有原型。通俗来讲,它们没有“爹”。空对象,顾名思义,一切皆空,什么也不是,什么也没有。而Object.prototype是原型链的最顶端,如果你是科班出身,那么你一定听说过链表,这里的Object.prototype也就相当于是链表的根节点。
做人要 追源溯果,写代码也一样。我们可以通过Object.creat方法创建指定原型的对象
var a = Object.create(Number.prototype)
a的父级就是Number,Number的父级是Object。下图可以辅助理解
此时,a便可以使用Number.prototype对象和Object.prototype中的方法。
操作属性
对象的属性还存在属性的特性用以控制对象的属性:值(value),操作性(configurable),可写性(writable),枚举性(numerable)。
var obj = { a: 1, b: 2 } Object.defineProperty(obj, 'b', { value: 5, enumerable:false }) console.log(obj) // {a: 1, b: 5} for (var i in obj) { console.log(i) // a }
configurable:能否使用delete、能否更改特性,false为不可重新定义
在此要注意,通过Object.defineProperty()创建的属性,以上三个特性的默认值都是false。
enumerable值为false的属性会影响三个过程:
1. 通过for in遍历对象的过程,无法遍历不可枚举属性
2.通过Object.keys(obj)返回属性值的过程,无法获得obj中的不可枚举属性名
3.JSON.stringify(obj)将对象转换为字符串的过程,只返回可枚举属性
检测属性
当我们需要检测属性时,可以通过以下三种方式:
var obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { value: 5, enumerable:false }) // 方法1 (可判断多有属性,包括继承的属性) console.log('a' in obj) // true // 方法2 (只可判断自身的属性) console.log(obj.hasOwnProperty('a')) // true // 方法3 (只可判断自身的可枚举属性) console.log(obj.propertyIsEnumerable('a')) // false console.log(obj.propertyIsEnumerable('b')) // true
删除属性
在删除属性时,delete只会断开属性与宿主对象之间的联系,而不是操作此属性。删除对象的属性时,要深层遍历防止属性值也为对象的情况。
var obj = { a: { x: 1 }, b: 0 } var obj2 = obj.a; delete obj.a console.log(obj) // {b: 0} console.log(obj2.x) // 1
存取器属性
类似于外观模式下的get与set方法,get 返回相应的值,set则用于根据场景的不同选择性的设置属性值。
var stu = { name: 'kevin', age: 15, get change () { return this.name }, set change (name) { if (this.age >= 18) this.name = name else throw '未满18岁不可改名' } } console.log(stu.change) // kevin stu.change = 'pomelo' console.log(stu.name) // 抛出异常
注:
1.存取器属性是可以继承的
2.存取器属性不含有value和writable特性,get代替了value,而set则代替了writable