JavaScript面向对象二

一、理解篇:

1、每创建的一个函数都有一个prototype属性,该属性是一个对象,prototype默认有一个constructor属性,默认指向prototype对象所在函数的指针;
2、prototype对象中定义的属性和方法是实例化prototype所在函数对象所共享的,当prototype为字面量对象时,其constructor属性将指向Object构造函数;
3、在实例对象中默认有一个内部属性__proto__,该属性指向原型对象,即(构造函数中的prototype属性,实例对象的原型对象)
4、在一个函数被作为构造函数时,我们把在构造函数中定义的属性,方法称为实例属性和方法,把在prototype对象中定义的属性和方法,称为原型属性和方法;

二、属性篇:

isPrototypeOf:   检查某对象是否是另一对象的原型
hasOwnProperty: 检测一个属性是否存在于实例中
in:             只要属性能够被实例所访问,就返回true,无论属性是否存在原型还是实例中
for-in:         通过for循环,返回所有可以枚举的属性(在ie6中存在一个bug,即被打上[[DontEnum]]不可枚举的标签属性不会被返回,而其他浏览器则屏蔽了不可枚举的属性,即也会被返回)

三、模式篇:

1、工厂模式
1
2
3
4
5
6
7
8
9
10
11
function obj(arg1,arg2){
    var o = new Object();
    o.arg1 = arg1;
    o.arg2 = arg2;
    o.method = function(){
        console.log(arg1+arg2);
    }
    return o;
}
var obj1 = obj(10,20);
obj1.method();  //30
缺点:对象不识别类型


2、构造函数模式
1
2
3
4
5
6
7
8
9
function Obj(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.method = function(){
        console.log(arg1+arg2);
    }
}
var obj1 = new Obj(10,20);
obj1.method();  //30
缺点:方法不能重用


3、原型模式
1
2
3
4
5
6
7
8
9
10
11
12
13
function Obj(){}
Obj.prototype = {
    constructor: Obj,
    arg3: ["a","b","c"],
    method: function(){
        console.log(this.arg3.toString());
    }
}
var obj1 = new Obj();
obj1.method();  //a,b,c
var obj2 = new Obj();
obj2.arg3.push("d");
obj1.method();  //a,b,c,d
缺点:原型对象存在引用类型属性时,此属性在实例对象上的修改会影响到另一个实例的共享属性


4、组合使用构造函数模式和原型模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Obj(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.arg3 = ["a","b","c"];
}
Obj.prototype = {
    constructor: Obj,
    method: function(){
        console.log(this.arg1+this.arg2);
    }
}
var obj1 = new Obj(10,20);
obj1.method();  //30
var obj2 = new Obj();
obj2.arg3.push("d");
console.log(obj1.arg3.toString());  //a,b,c
优点:在不涉及继承时,此模式为最佳模式


5、动态原型模式
1
2
3
4
5
6
7
8
9
10
11
function Obj(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
    if(typeof this.method != "function"){
        Obj.prototype.method = function(){
            console.log(this.arg1+this.arg2);
        }
    }
}
var obj1 = new Obj(10,20);
obj1.method();  //30
缺点:在实例化对象后不能再重写原型,否则会切断现有原型与实例的关系


6、寄生构造函数模式
1
2
3
4
5
6
7
8
9
10
11
function Obj(arg1,arg2){
    var o = new Object();
    o.arg1 = arg1;
    o.arg2 = arg2;
    o.method = function(){
        console.log(this.arg1+this.arg2);
    }
    return o;
}
var obj1 = new Obj(10,20);
obj1.method();  //30
缺点:与工厂模式类似,不建议使用该模式


7、稳妥构造函数模式
1
2
3
4
5
6
7
8
9
function Obj(arg1){
    var o = new Object();
    o.method = function(){
        console.log(arg1);
    }
    return o;
}
var obj1 = Obj(10);
obj1.method();  //10
说明:此模式只能通过method方法才能访问arg1变量,即:目的是为了在安全环境中执行


8、原型链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Super(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.arg3 = ["a","b","c"];
}
Super.prototype = {
    constructor: Super,
    superMethod: function(){
        console.log(this.arg3.toString());
    }  
}
function Sub(){
     
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
 
var obj1 = new Sub();
obj1.superMethod();  //a,b,c
var obj2 = new Sub();
obj2.arg3.push("d");
obj1.superMethod();  //a,b,c,d
缺点:只要是原型对象中存在引用类型属性,那么就会出现单独使用原型模式遇到的问题


9、借用构造函数
1
2
3
4
5
6
7
8
9
10
function Super(){
    this.arg3 = ["a","b","c"];
}
function Sub(){
    Super.call(this);
}
var superObj = new Super();
superObj.arg3.push("d");
var obj1 = new Sub();
console.log(obj1.arg3.toString());  //a,b,c
说明:在一个构造函数中调用另一个构造函数,即:在一个作用域中执行另一个作用域中要执行的代码


10、组合继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Super(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.arg3 = ["a","b","c"];
}
Super.prototype = {
    constructor: Super,
    superMethod: function(){
        console.log(this.arg3.toString());
    }  
}
function Sub(){
    Super.call(this,10,20);  //在实例化对象时,此句会调用超类构造函数
}
Sub.prototype = new Super();  //第一次调用超类构造函数
var obj1 = new Sub();
obj1.superMethod();  //a,b,c
var obj2 = new Sub();  //第二次调用超类构造函数
obj2.arg3.push("d");
obj1.superMethod();  //a,b,c
说明:此模式是继承原型链与借用构造函数模式中的优点,但也有缺点,即:在每次实例化子类对象时,都会调用两次超类构造函数


11、原型式继承
1
2
3
4
5
6
7
8
9
10
11
12
13
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var o = {
    arg1: 10,
    method: function(){
        console.log(this.arg1);
    }
}
var obj1 = object(o);
obj1.method();
说明:注意object函数返回的是以对象o为原型对象的实例对象


12、寄生式继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var o = {
    arg1: 10,
    method: function(){
        console.log(this.arg1);
    }
}
function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
        alert("hi");   
    }
    return clone;
}
var obj1 = createAnother(o);
obj1.sayHi();   //hi
说明:与原型式继承模式类似,只不过在返回的实例对象上增加了行为


13、寄生组合式继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);    //获得以superType.prototype为原型对象的临时对象,因此临时对象拥有这个原型对象的所有属性和方法
    prototype.constructor = subType;                //为prototype这个临时对象初始化constructor属性值
    subType.prototype = prototype;                  //将子类的prototype赋值为这个临时对象,因此子类的实例化对象共享这个临时对象(即:也共享超类构造函数中的prototype属性)中的所有属性和方法
}
function Super(arg1,arg2){
    this.arg1 = arg1;
    this.arg2 = arg2;
}
Super.prototype = {
    constructor: Super,
    superMethod: function(){
        console.log(this.arg1+this.arg2);
    }  
}
function Sub(arg1,arg2){
    Super.call(this,arg1,arg2);
}
inheritPrototype(Sub,Super);
var obj1 = new Sub(10,20);
obj1.superMethod();  //30
console.log(obj1.constructor);  //Sub(arg1,arg2){ xxx }
 
var superObj = new Super();
console.log(superObj.constructor);  //Super(arg1,arg2){ xxx }
说明:此模式是继承组合式继承和寄生式继承的优点,该模式为最佳模式


14、jQuery核心框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var $ = Fn = jQuery = function(){
    return new Fn.prototype.init();
}
Fn.prototype = jQuery.prototype = {
    init: function(){
        return this;
    },
    method1: function(){
        console.log("method1");
    },
    method2: function(){
        console.log("method2");
    }
}
Fn.prototype.init.prototype = Fn.prototype;
说明:
    以上代码不是上面任何模式中的一种,也没有涉及到选择器,只是模拟jQuery核心框架代码,该代码中涉及到函数返回值,原型对象以及继承。
    首先,jQuery指定的函数中返回一个实例化对象,这个返回的实例化对象是以jQuery函数中的原型属性中的init方法作为构造函数,init方法返回this,
此this并不会污染上一级作用域,因为它指向的是实例化init构造函数的对象,即jQuery函数中返回的对象;
    将init方法的原型属性赋值为jQuery函数的原型属性,那么jQuery函数的返回对象就会共享jQuery函数的原型属性中的属性和方法。

使用方法:
1
2
var obj = $();
obj.method1();  //method1

[注:此文章是总结JavaScript高级程序设计第二版中第六章面向对象]

 

posted @   杨君华  阅读(2155)  评论(3编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示