function factorial(num){
if(num<=1){
return 1;
}else{
return num = num * factorial(num-1);
}
}
改进
function factorial(num){
if(num<=1){
return 1;
}else{
return num = num * arguments.callee(num-1);
}
}
var factorial = function f(num){
if(num<=1){
return 1;
}else{
return num = num * f(num-1);
}
}
有权访问私有变量和私有函数的公有方法称为特权方法
function person(name){
this.getName = function () {
return name;
}
this.setName = function (value) {
name = value;
}
在构造函数中定义特权方法必须使用构造函数模式来实现这个目的
缺点 针对每个实例都会创建同样一组新方法
静态私有变量实现特权方法
(function(){
var name = "";
Person = function (value) {
name = value;
}
Person.prototype.getName = function () {
return name;
}
Person.prototype.setName = function (value) {
name = value;
}
})()
使用原型增进了代码复用 但是每个实例都没有自己的私有变量
模块模式 为单例创建私有变量和特权方法
var singleton = function () {
//私有变量和函数
var components = new Array();
//初始化
components.push(new BaseComponents);
//公共
return {
getComponentCount : function(){
return components.length;
}
registerComponent : function(component){
if(typeof component =="object"){
components.push(component);
}
}
}
} ();
增强模块模式 适用于单例必须是某种类型的实例
var application = function () {
//私有变量和函数
var components = new Array();
//初始化
components.push(new BaseComponent());
//创建application的一个局部副本
var app = new BaseComponent();
//公共
app.getComponentCount = function(){
return components.length;
}
app.registerComponent = function(component){
if(typeof component =="object"){
components.push(component);
}
}
return app;
}();
创建对象
工厂模式
function createPerson(name, age, job){
var o = new object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
构造函数模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
//等价于 this.sayName = new Function ("alert(this.name)");
}
Person大写开头
构造函数意味着将来可以把它的实例标识为一种特定的类型
缺点:每个方法都要在每个实例上重新创建一遍
原型模式
function Person(){
Person.prototype.name = name;
Person.prototype.age = age;
Person.prototype.job = job;
Person.prototype.sayName = function(){
alert(this.name);
}
}
[[prototype]] __proto__ isPrototypeOf() es5 Object.getPrototypeOf(0)
这个指针链接存在于实例和构造函数的原型对象之间
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
for in 返回所有能偶通过对象访问的 可枚举的属性
ie早期bug 屏蔽不可枚举属性的实例属性不会出现在for in ex:toString()
取得对象上所有可枚举的实例属性
Object.keys()
取得所有实例属性 无论是否可枚举 Object.getOwnPropertyNames() ex:constructor
function Person(){}
Person.prototype = {
//constructor :Person, 但是其[[Enumerable]]为true 默认是false的
name : "zz",
age : 25,
job : "Software Engineer"
sayName : function(){
alert(this.name);
}
}
注意: var friend = new Person(); friend.constructor == Object 因为重写了默认的prototype对象
可以修改原生对象的原型 但是不建议 可能导致命名冲突 也可能意外的重写了原生方法
问题·:原型模式省略了为构造函数传递初始化参数,结果所有实例在默认情况下取得相同的值
对于包含引用类型值得属性就会出问题 相互影响 如数组
组合模式 构造函数和原型
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["aa","bb"]; //放在原型中就出现上面问题
}
Person.prototype = {
constructor :Person,
this.sayName = function(){
alert(this.name);
}
动态原型
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
这里对原型的修改立即会在所有实例中得到反应
动态原型 不能使用对象字面量重写原型 重写会切断指针联系
寄生构造函数模式
function Person(name, age, job){
var o = new object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
特殊情况下用来为对象创建构造函数,如创建一个具有额外方法的特殊数组
返回的对象和构造函数或者构造函数的原型没有关系 不能依赖instanceof 一般不建议使用
稳妥构造函数模式
function Person(name, age, job){
var o = new object();
o.sayName = function(){
alert(name);
}
return o;
}
安全环境适用 不引用this 不用new操作符调用
除了sayName 没有办法再访问到name
继承
原型链 终点Object.prototype
instanceof isPrototype()
子类定义方法或重写超类方法一定要放在替换原型语句后
即:SubType.prototype = new SuperType();之后
不要使用字面量创建原型方法
一样存在引用值共享 无法向超类传递参数的问题
借用构造函数
function SuperType(name){
this.name = name;
}
function subType(){
//继承了SuperType 同时传递了参数
SuperType.call(this, "zz");
this.age = 25;
}
仅构造函数无法避免方法都在构造函数中定义了 无法函数复用
组合继承
function SuperType(name){
this.name = name;
this.colors = ["red","blue"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(){
//继承了SuperType
SuperType.call(this, name); //第二次
this.age = 25;
}
//继承方法
SubType.prototype = new SuperType(); //第一次
//创建子方法
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
无论什么情况下都会调用两次超类构造函数 不得不在调用子类型构造函数时重写这些属性
原型式继承
function object(o){
function F(){};
F.prototype = o;
return new F();
}
本质上object()对传入的o完成了一次浅复制 前提必须有一个对象可以作为另一个对象的基础
ES5 Object.create()方法规范原型式继承
两个参数 一个用做新对象原型的对象 一个为新对象定义额外属性的对象
一样有引用类型值问题
寄生式继承
function createAnother(original){
var clone = object(original);
//以某种方式来增强这个对象
clone.sayHi = function(){
alert("hi");
}
return clone;
}
主要考虑对象而不是自定义类型和构造函数下适用
不能复用
寄生组合式继承
解决组合继承调用两次超类构造函数(第二次调用产生的属性屏蔽了第一次调用原型中的同名属性)
不必为了指定子类型的原型而调用超类型的构造函数 所需的无非是超类型原型的副本
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype);
//弥补重写原型而失去的默认constructor属性
prototype.constructor = subType;
subType.prototype = prototype;
}
unction SuperType(name){
this.name = name;
this.colors = ["red","blue"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
只调用了一次SuperType 避免了在SubType.prototype上创建不必要、多余的属性
同时原型链保持不变 正常使用instanceof isPrototype()
YUI的YAHOO.lang.extend() 采用了