代码改变世界

由javascript对象合并联想到对象的创建方式

2013-05-14 20:01  Wid纬度  阅读(341)  评论(0)    收藏  举报

最近手头上正在进行的一个产品,有一个功能(比如留言)可以在很多页面上进行操作。

现在大体来讲有这样几种方式

1:A页面(用户列表页),点击留言 在用户信息下面显示输入留言框,点击确定则完成提交留言功能,提示留言成功,之后的影响有隐藏留言输入框,留言内容不在该页显示

2:B页面(留言列表页面),点击给不同用户留言,则显示一个留言框,点击确定完成提交留言功能,提示留言成功,之后的影响主要有,在不刷新页面的前提下,将留言内容通过脚本的方式插入到留言列表最上面。留言内容模版配置在xml中,通过后台处理返回

3:B页面(留言列表页面),点击回复(也是留言),则显示一个回复框,点击确定完成提交回复功能,提示留言成功,之后的影响主要有,在不刷新页面的前提下,将回复内容通过脚本的方式插入到当前留言回复列表最上面。同样回复内容模版配置在XML中,通过后台处理返回

4:其它页,弹出层的方式进行留言

首先先不管这样设计是否DT,总结一点:同一个操作(添加留言)有不同的影响,反应到后台,就是通过不同的参数进行不同的处理。目前的脚本处理大概如下

公共请求脚本

/*=============== 公共请求 ==================*/
        Async.fn.getScholarRequest = function (urlParams, datatype, callback, afterCallback) {
            this._asyncRequest("GET", "/DataCenter/Scholar.ashx",
            urlParams, datatype, callback, afterCallback);
        }
        Async.fn.postScholarRequest = function (urlParams, datatype, callback, afterCallback) {
            this._asyncRequest("POST", "/DataCenter/Scholar.ashx",
            urlParams, datatype, callback, afterCallback);
        }
        // 异步发送请求
        Async.fn._asyncRequest = function (type, url, urlParams, datatype, callback, afterCallback) {
            $.ajax({
                type: type,
                url: url,
                data: urlParams,
                dataType: datatype,
                cache: false,
                timeout: 90000,
                success: function (msg) {
                    if (msg === "OFF") {
                        showTips("请登录", null, null);
                        WinBox('用户登录', 'iframe:/Login/AjaxLogin.htm?rf=1', 430, 250, 'true', '', 'true', '', '');
                    }
                    else {
                        callback(msg);
                    }
                    //console.log(urlParams);
                    if (typeof afterCallback !== "undefined" && typeof afterCallback === "function")
                        afterCallback(msg);
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert('非常抱歉,加载内容失败,请重试');
                }
            });
        }

留言方法定义

Scholar.fn.addMsg = function (params, callback) {
            var msg = encodeURIComponent($(".f-textarea").val());
            var urlParams = {
                id: "MR",
                m: "a",
                c: msg
            };
            $.extend(urlParams, params); //合并参数
            Async.postScholarRequest(urlParams, "text", function (msg) {
                if (msg === "fail") {
                    showTips("留言失败");
                }
                else {
                    showTips("留言成功");
                }
            }, callback);
        }

方法调用

//调用1: 
parent.Scholar.addMsg({ u: userId, i: -1 }, function () {
                            $(".f-textarea").val("");
                            $(".divMsg").hide();
                        });
//调用2:
 parent.Scholar.addMsg({ u: lst.attr("uid"), i: lst.attr("id"), o: "r", n: lst.attr("name") }, function (msg) {
                            $(".contantList", $(".replySub").closest(".contantBoxSub")).first().after(msg);
                            $(".replySub").remove();
                        });

调用方式不一,上面主要在定义addMsg是定义了一个callback回调用于对于不同的处理方式,在请求处理成功后对页面不同的操作。

问题出在:

$.extend(urlParams, params); //合并参数

这一句,说来惨愧,一开始并不知道Jquery自带有合并对象的实现,还特意自己实现了一个。 这一句话就是用一个或多个其他对象来扩展一个对象,返回被扩展的对象。

最初在考虑这个合并的时候,联想到javascript中对象的创建.虽然这样的联想的关联性并不太大,但是既然想到了又对对象的创建完全忘光了,还是准备准备写篇文章以加深印象,也可以供大家参阅。有不当之处欢迎指证。共同提高.下面来正式讲Javascript中对象的创建

两个概念:

类:面向对象的程序设计语言都有类的概念,通过类可以创建类的对象,而Javascript中没有类的概念

对象:javascript中的对象其实就是一组名值对,其中值可以是数据或函数

对象的创建方式一:

创建一个Object的实例,原后再为它添加属性和方法

var instance = new Object();
instance.prop1 = "prop1";
instance.prop2 = "prop2";
instance.showProp = function () {
    console.log(this.prop1);
}
//调用
instance.showProp();

这是一个非常简单的方式创建对象,但是对于使用Object创建很多对象,可能就没有办法避免写大量的代码

对象的创建方式二:

使用工厂模式来抽象对象的创建过程

function createObject(prop1, prop2) {
    var instance = new Object();
    instance.prop1 = prop1;
    instance.prop2 = prop2;
    instance.showProp = function () {
        console.log(this.prop1);
    }
    return instance;
}

var instance1 = createObject("instance1_prop1", "instance1_prop2");
var instance2 = createObject("instance2_prop1", "instance2_prop2");

instance1.showProp();
instance2.showProp();

工厂模式虽然可以无数次的调用用来创建对象,解决了创建多个相似的对象的问题,而避免写大量的代码,但是和第一种创建方式一样,对于创建后的对象的类型是没有办法识别的(具体是什么类型,比如通过这种方式,我想创建桌子的对象,虽然可以把桌子的一些属性长,宽,高等作为参数传递对object对象,但是最终产生出来的对象是object对象,而不是桌子对象)

对象的创建方式三:

使用构造函数的方式创建对象

// 构造函数以大写字母开头
function Employee(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.showJob = function () {
        console.log(this.job);
    }
}

// 通过new的方式创建对象
var employee1 = new Employee("张三", 25, "研发工程师");
var employee2 = new Employee("李四", 28, "高级研发工程师");

employee1.showJob();
employee2.showJob();

以构造函数创建对象实际会经历四个过程

1:创建一个新对象

2:将构造函数的作用域赋给新对象

3:执行构造函数的代码

4:返回新对象

对于构造函数创建的对象都会自动有一个constructor属性,该属性指向Employee.对于构造函数的方式创建对象可以明确的标识实例的类型。但是同样的构造函数并非完美的创建对象的方式。Employee来讲,name、age、job属性应该是每个实例独有的,但是方法showJob则可以是共享的,而对过构造函数的方式创建对象对于每个实例方法都是独有的,这其实并没有必要,因为有this对象在,根本不用执行代码前把函数绑定给特定对象上面。

对象的创建方式四:

prototype: 每个函数都有一个prototype(原型)属性,这个属性是一个对象,包含由特定类型的所有实例共享的属性和方法

通过原型对象来实现共享

function Employee() {
}
Employee.prototype.name = "张三";
Employee.prototype.age = 25;
Employee.prototype.job = "研发工程师";
Employee.prototype.showJob = function () {
    alert(this.job);
}

var employee1 = new Employee();
var employee2 = new Employee();
employee1.showJob();
employee2.showJob();

原型原理:

调用构造函数创建一个对象的实例后,该实例的内部将包含一个指针(内部属性,通常叫__proto__),指向构造函数的原型对象.特别注意该连接存在于实例与构造函数的原型对象之间,不是存在于实例写构造函数(对象)之间。如下图

图片1

上图反应了对象、对象实例与对象原型对象之间的关系。

当访问一个属性时,实质上是执行一次搜索过程。搜索步骤如下

1:搜索对象实例本身,如果在实例本身找到了查找的属性,则返回属性值,否则继续第2步

2:搜索对象的原型对象,如果在对象的原型对象中找到了查找的属性,则返回属性值,否则继续第3步

3:继续搜索原型对象直到Object为止,如果打到查找的属性,则返回属性值,否则返回undefined

以上搜过过程正是原共享属性和方法的基础。

虽然可以通过对象实例访问保存在原型中的值,但是却不能通过对象实例重写原型中的值,但是可以通过在对象实例中添加同名属性,再利用搜索过程来达到屏蔽访问原型对象的效果。但是添加的同名属性对于对象原型中同名的属性没有任何影响。

原型的写法还有另外一种方式,就是用对象字面量来重写整个原型对象,如下:

function Emplyee() {
}
Employee.prototype = {
    constructor:Employee,
    name: "张三",
    age: 25,
    job: "研发工程师",
    showJob: function () {
        console.log(this.job);
    }
}

对于这两种方式的最根本的区别在于使用对象字面量会重写原型对象,使得构造函数(对象)的prototype属性指向了新的原型对象,如下图

在重写之前对象写对象原型之间的关系图如下:

图片2

在Employee.prototype = {}之后,对象与对象原型之间的关系图如下:

图片3

对象的prototype属性已经指向了新的原型对象了。使用这种方式重写原型,在重写之前如果有产生对象的实例,那么对象的实例不会指向新的原型对象。如下图

function Employee() {
}
// 在重写原型之前创建对象的实例
var employee1 = new Employee();

Employee.prototype = {
    constructor:Employee,
    name: "张三",
    age: 25,
    job: "研发工程师",
    showJob: function () {
        console.log(this.job);
    }
}

图片4

所以重写原型将会切断对象与对象最初原型对象之间的关系。而在重新原型之前创建的对象将无法访问新原型中的属性。

对象的创建方式五:

显然在创建方式四中存在着很多,比如省略了为构造函数传递参数的过程,导致的结果就是所有实例共共享所有属性。显然有时候这并不是我们期望的。另外还有一个特别严重的问题如下:对于引用类型的属性共识问题。如下

function Employee() {
}

Employee.prototype = {
    constructor: Employee,
    name: "张三",
    age: 25,
    job: "研发工程师",
    friends:["李四","王五"]
    showJob: function () {
        console.log(this.job);
    }
}
// 在重写原型之前创建对象的实例
var employee1 = new Employee();
employee1.friends.push("添加");
employee1.showJob();
var employee2 = new Employee();

对于employee2将会得到employee1添加之后的所有friends列表。如果期望不是这样的,那么对于原型这是一个致命的问题。另外我们写上面这样的代码也很奇怪,对于实例一般有自己独立的属性。我们可以利用构造函数和原型结合的方式来解决上面两个问题

function Employee(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["李四","王五"]
}
Employee.prototype = {
    constructor:Employee,
    showJob:function(){
        console.log(this.job);
    }
}
var employee1 = new Employee("张三", 25, "研发工程师");
var employee2 = new Employee("李四", 28, "高级研发工程师");

employee1.showJob();
employee2.showJob();

方式五是使用最为方便的一种方式,解决了上面的大部分存在的问题

对象的创建方式六:

function Employee(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["李四", "王五"];
    // 将原型的创建放在构造函数中
    if (typeof this.showJob != "function") {
        Employee.prototype.showJob = function () {
            console.log(this.job);
        }
    }
}

上述代码将原型的初始化放在构造函数中,这样也是一种灵活的创建对象的方式,但是这样创建对象存在着一个问题,不能按下面的方式写

function Employee(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["李四", "王五"];
    // 将原型的创建放在构造函数中
    if (typeof this.showJob != "function") {
        Employee.prototype = {
            showJob:function(){
                console.log(this.job);
            }
        }
    }
}

可以根据上面讲的,想想为什么不能这要写

 

对象的创建大概主要包括以上几种当然还有一些其它的一些并不常用的方式。对象的创建可以发现主要还是建立在Object、构造函数以及prototype等基础之上,其它的一些创建对象的变种也基本都是基于这些基本的概念之上。

 

今天就先写到这。讲到了对象的创建那么Javascript中还有另外一个非常重要的知识点---- 继承(不同于面向对象程序设计语言的继承)也打算有时间写一写