题目
- 如何准确判断一个变量是数组类型
- 写一个原型链继承的例子
- 描述new一个对象的过程
- zepto(或其他框架)源码中如何使用原型链
知识点
1. 构造函数
- 构造函数要用大写字母开头
- var a=其实是var a=new Object)的语法糖
- var a=[]其实是var a=new Array)的语法糖
- function Foo){..}其实是var Foo=new Function(.)
- 使用instanceof 可以判断一个函数是否是一个变量的构造函数
2. 原型规则和示例
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了“null”意外)
- 所有的引用类型(数组、对象、函数),都有一个proto(隐式原型)属性,属性值是一个普通的对象
- 所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通的对象
- 所有的引用类型(数组、对象、函数),_proto_属性值指向它的构造函数的”prototype”属性值
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找。
我们在一个构造函数的显式原型上去定义方法可以有效的减少内存占用,因为如果我们定义在构造函数内部,则每实例化一个对象,就会开辟一个堆内存去存放挂载到其实例上,然而对于方法的调用这是没有必要的
关于 prototype 它有以下几个要点,务必牢记:
- 每一个函数(类)都有原型属性,称作prototype,这个属性提供了可供当前类的实例调用的属性和方法。
- 浏览器默认给原型开辟的堆内存中有一个constructor属性,这个属性存放的是函数本身
- 每一个对象
的实例上都有一个proto属性称为原型链,这个属性指向当前类的所属原型,不确定的原型都指向Object.prototype,然而Object的proto指向null
prototype下的name属性指函数名,length属性指传入的形参的个数
利用 for in
循环可以来获取对象身上自己定义的属性而不获取来自原型的属性
3. 原型链
当一个方法在原型上没有时,就会查找原型链
4. intanceof
intanceof 用于判断 引用类型 属于哪个 构造函数 的方法。
finstanceofFoo的判断逻辑是:
- f的proto一层一层往上,能否对应到Foo.prototype,只要
f.__proto__ == Foo.prototype
就验证通过 - 再试着判断 f instanceof Object
1 | function Foo(name) { |
5. 关于原型重定向问题
先看下面的一个例子:
1 | function fun(){ |
结果:0 30
my_fun.a
用来设置私有属性
my_fun.__proto__.a
用来设置公有属性
原型重定向导致的问题:
- 自己开辟的堆内存中没有
constructor
属性,导致类的原型构造函数缺失(解决:自己手动在堆内存中增加constructor
属性) - 当原型重定向后,浏览器默认开辟的那个类原型堆内存会被释放掉,如果之前已经存储了一些方法或属性,都会丢失(所以:内置累的原型不允许重定向到自己开辟的堆内存,因为内置类的原型上存在很多属性方法,重定向后都没了,这样是不被允许的;但浏览器对内置类有保护机制)
- 当我们需要给类的原型批量设置属性和方法的时候,一般都是让原型重定向到自己创建的对象中
解题
1. 如何准确判断一个变量是数组类型
1 | var arr = []; |
2. 写一个原型链继承的例子
基础实例:
1 | //动物 |
封装DOM查询:戳我查看完整示例代码
1 | function Elem(id) { |
3. 描述new一个对象的过程
- 创建一个新对象
- 对新对象执行 [[prototype]] 连接
- this 指向这个新对象
- 执行代码,即对this 赋值
- 返回 this (这一步是默认的)