如何创建对象以及jQuery中创建对象的方式(推荐)

1. 使用对象字面量创建对象 key-value

var cat = {
    name: 'tom',
    info: this.name + ': 1212',
    getName: function() {
        return this.name;
    }
};

注意上例属性info中,使用了this.name,这里的this指向window对象,请尽量避免在定义对象属性时使用表达式,而将有表达式的内容写入到函数中。

2.使用new创建对象

var dog = new Object();
dog.name = 'tim';
dog.getName = function() {
    return dog.name;
}
  • 可以使用delete删除对象的属性和方法
delete dog.name;

在window作用域中,不能使用delete删除var, function定义的属性和方法,可以删除没有使用var, function定义的属性和方法

3. 工厂模式

在实际使用当中,字面量创建对象虽然很有用,但是它并不能满足我们的所有需求,我们希望能够能够和其他后台语言一样创建一个类,然后声明类的实例就能够多次使用,而不用每次使用的时候都要重新创建它,于是,便有了工厂模式的出现。

function person(name) {
    var o = new Object();
    o.name = name;
    o.getName = function() {
        return this.name;
    }
    return o;
}
var person1 = person('rose');
var person2 = person('jake');

这种模式在函数的内部创建了一个空对象,然后逐一添加属性和方法,最后返回,实现了对象得以复用的目的。但是存在2个很大的问题

  • 无法识别对象的类型
console.log(person1 instanceof person); // false
  • 每个对象调用的同名方法其实并不同一个方法
console.log(person1.getName == person2.getName); // false

其实就相当于每次声明对象都被重新创建,只不过写法上简单了一点而已。

4. 自定义构造函数

var Person = function(name) {
    this.name = name;
    this.getName = function() {
        return this.name;
    }
}

var person1 = new Person('tom');
var person2 = new Person('tim');

使用var或者function声明函数都可以,只是我写例子的时候想到什么就写了什么,这个区别在这里不是重点

和工厂模式相比,自定义构造函数没有在函数内部显示的创建和返回对象,而是使用this,当然,看上去简洁了许多,那么它解决了工厂模式的什么问题呢?

console.log(person1 instanceof Person);  // ture
console.log(person1.getName == person2.getName); //false

从上面代码可以看出,对象的类别可以判断了,person1就是Person的对象,可是2个同名方法任然不是同一个方法,而是重新创建,其实构造函数内部的实现,可以将上面的代码写成这样来理解

var Person = function(name) {
    var this = {};
    this.name = name;
    this.getName = function() {
        return this.name;
    }
    return this;
}

看上去和工厂模式就有点像,只是this的声明和返回都是隐式的。下一步,我们将要介绍关键先生

原型

原型并没有那么神秘,因为在javascript中,它无处不在。为了了解原型,我们可以在chrome浏览器的console中,随意创建一个函数

function a(){}

然后继续输入

a.prototype

得到的结果如下

a {
    constructor: function a(),
    _proto_: Object
}

没错,得到的这个a,就是关键先生原型了。每一个函数都有一个proportype属性,他就像一个指针一样指向它的原型,而每一个原型,都有一个contructor属性,指向他的构造函数。 那么原型在创建对象中有什么用呢? 一个例子,千锤百炼,如下

 function Person(name) {
     this.name = name;
 }
 Person.prototype.getName = function() {
     return this.name;
 }

 var person1 = new Person('rose');
 var person2 = new Person('Jake');

在这里,我们将属性写在了构造函数里,将令人头疼的方法写在原型里,看看上面的问题得到解决没有

console.log(person1 instanceof Person); // true
console.log(person1.getName === person2.getName); // true

OK,问题完美解决。

当然也可以将属性写入原型中,但是如果那样的话,属性就会如同方法一样被公用了,因此一般来说,属性会写入构造函数之中,方法写入原型之中。当然,这视情况而定。

如果你想进一步了解原型,可以看下图。

当我们使用new person时便会创建一个实例,比如这里的person1与person2,这里的实例中,会有一个_proto_属性指向原型。

  • 原型中的查找机制

当我们使用实例person1调用方法person.getName( )时,我们首先找的,是看看构造函数里面有没有这个方法,如果构造函数中存在,就直接调用构造函数的方法,如果构造函数不存在,才回去查找原型中是否存在该方法

function Cat(name) {
    this.name = name;
    this.age = 12;
    this.getName = function() {
        return 'constructor';
    }
}
Cat.prototype.name = 'proto name';
Cat.prototype.getName = function() {
    return this.name;
}

var tim = new Cat('Tim');
console.log(tim.name);  // tim
console.log(tim.getName());  // constructor

可以看到上例中,当原型和构造函数中拥有同样的方法和属性的时候,构造函数中的被执行。 于是,这里便会有一个十分重要的概念需要理解,那就是this的指向问题。 在整个创建对象的过程当中,this到底指向谁?

 function Person(name) {
     this.name = name;
    //  this.getName = function() {
    //      return 'constructor';
    //  }
 }
 Person.prototype.getName = function() {
     return this.name;
 }
 Person.prototype.showName = function() {
     return this.getName();
 }

var rose = new Person('rose');
console.log(rose.showName()); //rose

其实在new执行时,构造函数中的this与原型中的this都被强行指向了new创建的实例对象。

如果需要写在原型上的方法很多的话,还可以这样来写,让写法看上去更加简洁

Person.prototype = {
    constructor: Person,
    getName: function(){},
    showName: function(){},
    ...
}

constructor:Person这一句必不可少,因为{}也是一个对象,当使用Person.prototype = { }时,相当于重新定义了prototype的指向,因此手动修正{}的constructor属性,让他成为Person的原型。

5. jQuery中创建对象是如何实现的?

其实通过上面方式,使用构造函数声明实例的专属变量和方法,使用原型声明公用的实例和方法,已经是创建对象的完美解决方案了。可是唯一的不足在于,每次创建实例都要使用new来声明。这样未免太过麻烦,如果jquery对象也这样创建,那么你就会看到一段代码中有无数个new,可是jQuery仅仅只是使用了$('xxxx')便完成了实例的创建,这是如何做到的呢?

还是按照惯例,先贴代码。

 (function(window, undefined) {
     var Person = function(name) {
         return new Person.fn.init(name);
     }

     Person.prototype = Person.fn = {
         constructor: Person,

         init: function(name) {
             this.name = name;
             return this;
         },

         getName: function() {
             return this.name;
         }
     }
     Person.fn.init.prototype = Person.fn;
     window.Person = window.$ = Person;
 })(window);

 console.log($('tom').getName());

一步一步来分析

  • 首先为了避免变量污染,使用了函数自执行的方式。这种方式让javascript代码具备了模块的特性,因此大多数js库都会这样做
(function(){
    ...
})()

传入window参数,是为了让jquery对象在外window中可以被访问,因此有如下一句代码

window.Person = window.$ = Person;

这样我们就可以直接使用"$"来调用Person构造函数

  • 关键问题在于,真正的构造函数并不是Person,而是Person原型中的init方法。其中的复杂关系,我们借助下图来分析了解,表达能力实在有限,也不知道如何才能表达的更加简洁易懂。

当外部调用$( ).getName( )时,函数内部的执行顺序如下

new Person.fn.init()
// 而init的原型,通过下面一句指向了Person的原型
Person.fn.init.prototype = Person.fn;
// 于是就可以调用原型中的getName方法了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小白的技术客栈

Python之解析式您知多少?

解析式 解析式 今天给大家介绍Python中的解析式。 ? 解析式简单介绍 解析式, 列表解析 # Python2只有列表解析 生成器解析 # Python...

4455
来自专栏极客猴

Python中“is”和“==”的区别

相比 C/C++ 、Java 等强类型语言, Python 定义变量的方式就简单多了。我们只需要给变量起个变量名,而不需要给变量指定类型。

972
来自专栏简书专栏

Python入门2

定义:列表是最常用的python数据类型,一个列表中可以存储多个不同类型的数据。 语法:[]可以是number、string、bool等

1301
来自专栏java一日一条

java提高篇之关键字static

在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员...

902
来自专栏锦小年的博客

python学习笔记7.3-内建模块collections

Python的内建模块collections为我们提供了一系列的优化操作,本质上就是元组、字典、集合、列表的特殊功能版。 1. namedtuple name...

2205
来自专栏老马说编程

计算机程序的思维逻辑 (2) - 赋值

赋值 上节我们说了数据类型和变量,通过声明变量,每个变量赋予一个数据类型和一个有意义的名字,我们就告诉了计算机我们要操作的数据。 有了数据,我们能做很多操作。但...

2105
来自专栏Laoqi's Linux运维专列

文件类型+变量+数值字符串

2016
来自专栏java达人

JSON.stringify(), JSON.parse(), toJSON()方法使用

1 JSON.stringify() 将value(Object,Array,String,Number...)序列化为JSON字符串 即:把原来是对象的类型...

28010
来自专栏小勇DW3

Java之static作用的全方位总结

 引用一位网友的话,说的非常好,如果别人问你static的作用;如果你说静态修饰 类的属性 和 类的方法 别人认为你是合格的;如果是说 可以构成 静态代码块,...

1402
来自专栏有趣的django

6.python内置函数

1. abs() 获取绝对值 >>> abs(-10) 10 >>> a = -10 >>> a.__abs__() 10 2. all()   参数为可迭代对...

3556

扫码关注云+社区

领取腾讯云代金券