前言
js里面几乎一切都是对象
什么是对象
ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数
为什么说js里面几乎一切都是对象
这里为什么说几乎,因为不包括null和undefined。其他的,包括字符串、数字、布尔值、数组甚至是函数都有属性和方法,所以他们都是对象。
js里面的数据类型
基础类型
var a = 123 |
a,b,c属于基础类型
包装类型
var d = [] |
d,o,f属于包装类型
基础包装类型
var x = new Number(123) |
x,y,z属于包装类型
上面说js里面字符串、数字、布尔值都是对象,都拥有属性和方法,我们尝试给基本类型的数据添加属性
var a = 123 |
这是怎么回事,不是都是对象吗,为什么不能动态添加属性和方法。
其实我们之所以能访问到基本类型数据的属性和方法
var a = 123 |
是因为在我们访问的一瞬间,js帮我们把基础类型转化成它对应的基础包装类型,访问结束会销毁掉这个基础包装类型对象,这也是为什么不能给基础类型的数据动态添加属性和方法的原因。
类似下面这个过程
var a = 123 |
js里的函数
js里面函数是第一等公民,为什么这么说?
因为函数也是对象,自然有属性和方法。函数还可以赋值给一个变量,可以塞到一个数组中,可以作为其他对象的属性,可以作为参数传递给另一个函数。其他数据能做的它能做,其他数据不能做的它也能做,简直无所不能,所以说函数是第一等公民。
js里的继承
new操作符
js里每个引用类型的对象都有一个proto属性,每个函数都有一个prototype属性,所以函数这种既是函数又是引用类型的数据类型就同时拥有proto与prototype。
假如有这样一个函数
function f(a,b){ |
对f函数使用new操作符后,到底发生了什么。我们先不用new 操作符,直接调f函数
function f(a,b){ |
可见f函数里面的this默认指的是window,对f函数使用new操作符后,它先生成了一个空对象o,然后让this指向o,刚才说了javascript里面每个对象都有一个proto属性,o也不例外,再把f函数的prototype属性指向的引用赋值给o对象的proto,然后执行this.a = 1;this.b = 2,最后返回这个对象。
这个过程类似于下面的代码
function f(a,b){ |
原型链
我们定义一个数组[],我们并没有赋给它push、pop等方法,为什么它可以调用这些方法,秘密就在proto属性上,proto属性指向该对象的原型,说白了,就是一个对象,这个对象中存有生成这个数组的构造函数和一些公用方法。这就是为什么我们没有给[]任何属性和方法,它却可以调用push、pop的原因。一个对象如果本身不含有某个方法或者属性,就会查找它的原型,找到了就执行方法,找不到就继续查找原型。对象的原型也是一个对象,是对象就会有原型,所以我们的原型链最终会指向Object的prototype。
说了这么多,用代码表示这一过程
var arr = [] |
instanceof
a instanceof b 表示a是否是b的实例
[] instanceof Array // true |
它的实现原理:判断a的原型链上是否有b的prototype
图太长,截不下
[] instanceof Array // true |
因为[]的原型链上有Array和Object的prototype
写到这里,突然想到用[] instanceof Array判断一个对象是否是数组有点不靠谱。假如有这样一个对象
var o = { |
假如有人这样造一个假数组,instanceof就不准了,所以判断对象是否是数组还是用Array.isArray([])比较靠谱。
前两天在知乎上看到这样一道题:
有兴趣的话可以去围观
如果让我来解释的话:
[] instanceof Array // true,[].__proto__==Array.prototype |
[]的原型链
Function的prototype
在[]的原型链上找不到Function的prototype,所以说[] instanceof Function 的值为false
参考资料
javascript面向对象编程指南