一、构造函数
概念:类似于模板引用的机制
- new一个构造函数,返回对象的过程
//注意函数的首字母大写
function Foo(name,age){ //传入2个参数
this.name = name //对name进行赋值
this.age = age //对age进行赋值
this.class = 'class-1'
// return this // 默认有这一行
}
var f = new Foo('zhangsan',18)
//通过Foo这个构造函数可以new出很多个不同的实例
// eg: var f1 = new Foo('lidi',19)
定义 f 赋值为 new,new 是一个构造函数形成实例的过程;然后把字符串 'zhangsan'
、数字 20
传进去。
- new一个对象的过程
① new 的时候把参数传进去(也可以不传);
② new 之前,函数里面的 this 会先变成一个空对象;
③ 然后进行属性的赋值(例如:this.name=name
);
④ 赋值完之后,再默认把this
给return
回来;
⑤ return 回来之后赋值给 f ,此时,f 具备了属性
f.name=zhangsan;
f.age=18;
f.class = class-1
-
简述new一个对象的过程
① 创建一个新对象
② this 指向这个新对象
③ 执行代码,即对 this 赋值
④ 返回 this -
this
通过对象的属性的形式来执行函数时,无论是函数自身的属性还是函数原型中得到的属性,函数中的this永远指向对象f自身 -
扩展
①var a={}
是var a = new Object()
的语法糖 (推荐前者写法)
②var a=[]
是var a = new Array( )
的语法糖(推荐前者写法)
③function Foo( ){…}
是var Foo = new Function(…)
的语法糖(推荐前者写法) -
instanceof
用于判断引用类型属于哪个构造函数的方法 -
instanceof Array
判断一个变量是否为数组
二、原型规则
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了
null
以外)
var obj = { }; obj.a = 100; // 定义了一个对象,对象扩展属性a
var arr = { }; arr.a = 100; // 定义了一个数组,数组扩展属性a
function fn ( ){ } // 定义了一个函数
fn.a = 100; // 给函数增加属性值
- 所有的引用类型(数组、对象、函数),都有一个
__proto__
【隐式原型】属性,属性值是一个普通对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
- 所有的函数,都有一个
prototype
属性【显式原型】,属性值也是一个普通对象
console.log(fn.prototype)
- 所有的引用类型(数组、对象、函数),
__proto__
属性值指向它的构造函数的prototype
属性值
console.log(obj.__proto__ === Object.prototype)
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的
__proto__
(即它的构造函数的prototype
)中寻找。【 由于对象的隐式原型===
它构造函数的显式原型 】
// 定义一个构造函数
function Foo(name,age){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name) //this就是f
}
// 创建实例
var f = new Foo('zhangsan')
f.printName = function(){
console.log(this.name)
}
// 测试
f.printName( ) // 通过对象的属性的形式来执行函数时,无论是函数自身的属性还是函数原型中得到的属性,函数中的this永远指向对象f自身
f.alertName( )
- 扩展
this
通过对象的属性的形式来执行函数时,无论是函数自身的属性还是函数原型中得到的属性,函数中的 this 永远指向对象 f 自身
循环对象自身的属性
var item
for(item in f){
// 高级浏览器已经在for in中屏蔽了来自原型的属性
//但是这里建议加上这个判断,保证程序 的兼容性。
if( f.hasOwnProperty(item) ){
console.log(item)
}
/* 通过hasOwnProperty判断,如果通过,则是对象自身的属性;否则是来自原型中的属性 */
}
API: hasOwnProperty
:判断对象自身属性、兼容性
三、原型链
例子:toString()
// 构造函数
function Foo( ){
this.name = name
}
Foo.prototype.alertName = function( ){
alert(this.name)
}
// 创建示例
var f = new Foo('zhangsan')
f.printName = function( ){
console.log(this.name)
}
// 测试
f.printName( )
f.alertName( )
f.toString( ) // 要去f.__proto__.__proto__中查找
理解:根据第 5 条原型规则来看:
toString
,f
自身没有toString
这个属性,要去f自身的隐式原型中找;相当于要去其构造函数的显式原型中找,即Foo.prototype
;- 但
Foo.prototype
没有这个toString
属性,同时,Foo.prototype
属性值也是一个对象【规则3】,那么可以去Foo.prototype
这个属性值【对象】的隐式原型中找;因为【对象】的隐式原型也就等于构造函数Object
的显式原型,即Foo.prototype.__proto__ ===Object.prototype
- 此时,
Object.prototype
的原型是null
,null
没有任何属性和方法,也没有自己的原型;因此原型链的尽头就是null
Object
是有toString
属性的。
四、面试题
- 如何准确判断一个变量是数组类型
var arr = [ ]
arr instanceof Array // true
typeof arr // object, typeof是无法判断是否是数组的
- 写一个原型链继承的例子(必须掌握)
实战:可写一个封装DOM查询的例子
小例子:(不要在面试中写)
// 动物
function Animal(){ //定义一个构造函数Animal
this.eat = function(){ //定义一个eat属性
console.log('animal eat')
}
}
//狗
function Dog(){ //定义一个构造函数Dog
this.bark = function(){
console.log('dog bark')
}
}
Dog.prototype = new Animal() //构造函数Dog的显式原型已经有一个对象,把这个对象改掉,赋值成new Animal(); new Animal()返回出来是一个Animal new出来的一个对象,具有eat属性。
//即:把Dog这个显式原型,赋值成一个对象,这个对象有一个eat属性,
//哈士奇
var hashiqi = Dog()
- 描述 new 一个对象的过程(要知道原理)
① 创建一个新对象
② this 指向这个新对象
③ 执行代码,即对 this 赋值
④ 返回 this - zepto(或其他框架)源码中如何使用原型链(源码实现)
阅读源码是高效提高技能的方式,有技巧地读(先在网上找资料如何去读)
eg: 搜索 “jquery源码解读分析” ,先有铺垫,是什么样的结构;带着这些信息来读
eg: 慕课网搜索 " zepto 设计和源码分析 "
zepto、jquery、anderstoer 源码