1 原型/原型链
1-1 原型
定义:原型是function对象的一个属性,定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。
原型是一个对象。
可以利用原型的特性,提取共有属性。
对象属性与在原型上属性的增删改查。
对象如何查看原型 -> 隐式属性 __proto__
对象如何查看构造函数 -> constructor
1-2 原型链
原型链的构成(继承方法)。
原型链与原型属性的增删改查基本相同。
this指向调用该方法的对象。
大多数对象最终继承自Object.prototype
Object.create(proto)
原型方法的重写。
1-3 代码分析
Grand.prototype.lastName = "Chen";
function Grand(){}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.firstName = "Xianxian";
this.fortune = {
card1 : 'visa'
}
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobby = "play";
}
var son = new Son();
执行效果如下:主要为了验证修改子类原始值不会影响父类,而修改引用值则会影响父类。
2 命名空间
【解决问题】
为了解决多人合作开发过程中变量命名同名的问题,避免变量污染。
【实现方法】
采用立即执行函数,以init()作为入口,调用init()即可执行其中的内容。最后该立即执行函数要有返回结果。因此需要为该组件编写好接口,并制定好相应的接口规范。
返回函数相当于main入口,整体上形成闭包,立即执行函数内部的变量被私有化了。
【实现代码】
<script>
// namespace
var name = 'global';
var initChen = (function(){
var name = 'local';
function output(){
console.log(name);
}
return function(){
output();
}
}())
initChen();
</script>
代码执行结果为 'local'
【实现方法2】
使用with(obj){...}:可以将内部的代码的作用域链的顶端修改为obj,即在with内部可以访问obj中的变量,进而实现作用域链。
可以类比C++中的using namespace obj; obj::proc 来进行理解。
缺点:修改了作用域链,影响执行效率。
在es5中的严格模式"use strict"中被禁止使用。
比如使用某一个人编写的代码块则可以with(obj.department1.Chen){}内部访问的即都是obj.department1.Chen以下的属性变量。
或经常使用document.xxx则可以简化书写为with(document){ xxx }即可。
3 链式调用的实现原理
【问题引入】
模仿jQuery中的链式调用的实现。
【核心思想】
在对象中的每个方法都返回this,即可实现用调用方法的对象作为这个方法的返回结果放回到原来的位置上,这样一来,调用链就有恢复到了上一状态(即对象.调用方法()的状态)
【实现代码】
<script>
// chain call
var chen = {
study : function(){
console.log('I love studying!');
return this; // return obj chen
},
exercise : function(){
console.log('I love exercising!!');
return this;
},
read : function(){
console.log('I love reading!!!');
return this;
}
}
chen.study().exercise().read();
</script>
4 对象枚举
【对象属性访问原理】
obj.prop是常用的访问方式,但是在系统实现上是将其转化为了obj['prop']进行处理的。因此,我们可以知道,在对象属性访问的key索引中,都是做成了一个string的匹配索引问题。
【for_in循环】
采用for(var prop in obj)的形式,可以遍历到obj中的每一个key值,并将该key值以string的类型存放值prop中。
因此采用以下遍历访问形式是错误的:
obj.prop
执行这句会返回一个undefined,这是由于系统会自动转化为obj['prop']进行查找,若obj中没有prop为key的属性的话,那么自然就是一个undefined的结果。
正确的遍历访问形式是这样的:
obj[prop]
【hasOwnProperty】
遍历过程中,我们通常只希望得到该对象的私有属性,而不取得原型上的属性,因此在封装对象枚举方法的时候,我们通常首先增加一层hasOwnProperty的判断。
【实现代码】
<script>
// enumerate
var obj = {
height : "100px",
width : "100px",
border : "1px solid #ccc",
position : "absolute"
}
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
console.log(obj[prop]);
}
}
</script>
5 this
1.预编译时,会首先将this指向window
2.全局变量的this指向window
3.可以使用call()和apply()改变this的指向,传入的第一个参数为this指向的信息object
4.this指向调用该方法的对象,如obj.fn(),那么fn中的this都指向obj
6 callee和caller
1.arguments.callee:callee只能用于arguments参数列表,可以获取参数列表对应的被调用的函数。
主要作用:立即执行函数执行后即被销毁从而无从寻找,因此可借助callee进行记录,进而实现递归调用。
示例代码(递归计算一个10!):
<script>
// callee
var init = (function(n){
if(n == 1) return 1;
return n * arguments.callee(n - 1);
}(10))
console.log(init);
</script>
2.fn.caller:获取调用fn的函数