腊月的季节

关于JavaScript对象的知识点

instanceof操作符

通过instanceof操作符,我们可以测试一个对象是不是由某个指定的构造器函数所创建的。例如:
>>> function Hero(){}
>>> var h = new Hero(){};
>>> var o = {};
>>> h instanceof Hero;
true
>>> h instanceof Object;
true
>>> o instanceof Object;
true
请注意,这里的函数名后面没有加括号(即不是h instanceof Hero()),因为这里不是函数调用,所以我们只需要像使用其他变量一样,引用该函数的名字即可。

返回对象的函数

除了使用new操作符来调用构造函数以外,我们也可以抛开new操作符,只用一般函数来创建对象,这就需要一个能执行某些预备工作,并以对象为返回值的函数。
例如,下面就有一个用于产生对象的简单函数fanctory();

1
2
3
4
5
function factory(name){
return {
name: name
};
}

然后我们调用factory();
>>> var o = factory(‘one’);
>>> o.name
“onw”
>>> o.constructor
Object()
实际上,构造器函数也是可以返回对象的,只不过在this值得使用上会有所不同。这意味着我们需要修改构造器函数的默认行为。下面,我们来看看具体是怎么做。
这是构造器的一般用法:
>>> function C() {this.a=1};
>>> var c = new C();
>>> c.a
1
但现在要考虑的是这种用法:
>>> function c2() {this.a=1;return {b:2};}
>>> var c2= new C2();
>>> typeof c2.a
“undefined”
>>> c2.b
2
能看出来发生了什么吗?在这里,构造器返回的不再是包含属性a的this对象,而是另一个包含属性b的对象。但这也只有在函数的返回值是一个对象时才会发生,而当我们企图返回的是一个非对象类型时,该构造器将会照常返回this。

传递对象

当我们拷贝某个对象或者将它传递给某个函数时,往往传递的都是该对象的引用。因此我们在引用上所做的任何改动,实际上都会影响它所引用的原对象。
在下面的示例中,我们将会看到对象是如何赋值给另一个变量的,并且,如果我们对该拷贝做一些改变操作的话,原对象也会跟着被改变:

1
2
3
4
5
6
7
8
>>> var original = {howmany:1};
>>> var copy = original;
>>> copy.howmany
1
>>> copy.howmany = 100;
100
>>> original.howmany
100

同样的,将对象传递给函数的情况也大致如此:

1
2
3
4
5
>>> var original = {howmany: 100};
>>> var nullify = function(o) {o.howmany=0;}
>>> nullify(original);
>>> original.howmany
0

对象比较

当我们对对象进行比较操作时,当且仅当两个引用指向同一个对象时为true,而如果是不同的对象,即使他们碰巧拥有相同的属性和方法,比较操作也会放回false。
下面,我们来创建两个看上去完全相同的对象:

1
2
>>> var fido = {breed:'dog'};
>>> var benji = {breed: 'dog'}

然后,我们对它进行比较,操作将会返回false:

1
2
3
4
>>> benji === fido
false
>>> benji == fido
false

我们可以创建一个变量mydog,并将其中一个对象赋值给它。这样一来mydog实际上指向了这个变量。

1
var mydog = benji;

在这种情况下,mydog与benji所指向的对象是相同的(也就是说,改变myblog的属性就等同于改变benji),比较操作就会返回true。

1
2
>>> mydog === benji
true

并且,由于fido是一个不同的对象,所以它不能与mydog进行比较。

1
2
>>> mydog === fido
false

Object

Object是JavaScript中所有对象的父级对象,这意味着我们创建的所有对象都继承与此。为了新建一个空对象,我们既可以用对象文本标识法也可以调用Object()构造器函数,即下面这两行代码的执行结构是等价的:

1
2
>>> var o = {};
>>> var o = new Object();

所谓的空对象,实际上并非是完全无用的,它还是包含了一些方法和属性。下面,我们来看看其中的一小部分:

  • 返回构造器函数的构造属性。
  • 返回对象描述字符串的toString()方法。
  • 返回对象单值描述信息的valueOf()方法。一般情况下,返回的就是对象本身。现在来看看这些方法的实际应用。首先,我们来创建一个对象:
    1
    >>> var o = new Object();

然后调用toString()方法,返回该对象的描述字符串:

1
2
>>> o.toString()
"[object Object]"

toString()方法会在某些需要用字符串来表示对象的时候被JavaScript内部调用。例如alert()工作就需要用到这样的字符串。所以如果我们将对象传递给了一个alert()函数,toString()方法就会在后台被调用,也就是说,下面两行代码的执行结果是相同的:

1
2
>>> alert(o)
>>> alert(o.toString())

另一种会使用字符串描述文本的地方就是字符串连接操作,如果我们将某个对象与字符串进行连接,那么该对象就先调用自身的toString()方法:

1
2
>>> "An object:"+o
"An object:[object Object]"

valueOf()方法也是一个所有的对象所共有的方法。对于简单的额对对象来说(即构造器是Object的对象),valueOf()方法返回的就是对象自己。

1
2
>>> o.valueOf() === o
true

总而言之:

  • 我们创建对象时既可以用var o = {}的形式,也可以用var o = new Object()。
  • 无论是多复杂的对象,它都是继承自Object对象的,并且拥有其所有的方法和属性。

    Array

    Array()是一个用来构建数组的內建构造器函数,例如:
    1
    >>> var a = new Array();

这与下面的数组文本标识法是等效的:

1
>>> var a = [];

无论数组是以什么方式创建的,我们都能照常往里添加元素:

1
2
>>> a[0] = 1;a[1] = 2; a;
[1,2]

当我们使用Array()构造器创建数组时,也可以通过传值的方式为其设定元素。

1
2
3
>>> var a = new Array(1,2,3,'four');
>>> a;
[1,2,3,"four"]

但是如果我们传递给该构造器的是一个数字,就会出现一种异常情况,即该数值会认为是数组的长度。

1
2
3
>>> var a2 = new Array(5);
>>> a2;
[undefined,undefined,undefined,undefined,undefined]

既然数组是由构造器创建的,那么这是否意味着数组实际上是一个对象呢?的确如此,我们可以用typeof操作符来验证一下:

1
2
>>> typeof a;
"object"

由于数组也是对象,那么就说明它也继承了Object的所有方法和属性。

1
2
3
4
5
6
>>> a.toString();
"1,2,3,four"
>>> a.valueOf()
[1,2,3,"four"]
>>> a.constructor
Array()

尽管数组也是对象,但还是有一些特殊之处,因为:

  • 他们的属性名都是从0开始递增的,并自动生成数值
  • 它们拥有一个记录数组中元素数量的length属性。
  • 它们都是在父级对象的基础上扩展额外的內建方法。
    下面来实际验证一下对象与数组之间的区别,让我们从创建空对象o和空数组a开始:
    1
    >>> var a = [], o = {};

首先,定义数组对象时会自动生成一个length属性,而这在一般对象中是没有的。

1
2
3
4
>>> a.length
0
>>> typeof o.length
"undefined"

在为数组和对象添加数字和非数字属性方面,两者并没有什么区别:

1
2
>>> a[0] = 1;o[0]=1;
>>> a.prop = 2; o.prop = 2;

length属性通常会随着数字属性的数量而更新,而非数字属性则会被忽略。

1
2
>>> a.length
1

当然我们也可以手动设置length属性。如果设置的值大于当前数组中元素数量,剩下的部分会被自动创建空对象所填充。

1
2
3
4
>>> a.length-5
5
>>> a
[1,undefined,undefined,undefined,undefined]

而如果我们设置的length值小于当前元素数,多出的那部分元素会被移除:

1
2
3
4
>>> a.length -2 
2
>>> a
[1,undefined]

值得关注的数组方法
除了从父级对象那里继承的方法以外,数组对象中还有一些更为有用的方法,例如sort(),join()和slice()等。
下面,我们将通过一个数组来试验一下这方法:

1
>>> var a = [3,5,1,7,'test'];

push()方法会在数组的末端添加一个新元素,而pop()方法会移除最后一个元素,也就是说a.push(“new”)就相当于a[a.length]=”new”,而a.pop()则与a.length–的结果相同。
另外,push()返回的是改变后的数组长度,而pop所返回的是被移除的元素。

1
2
3
4
5
6
7
8
>>> a.push("new");
6
>>> a
[3,5,1,7,"test","new"]
>>> a.pop();
"new"
>>> a
[3,5,1,7,"test"]

而sort方法则是用于给数组排序的,它会返回修改后的数组,在下面的示例中,排序完成后,a和b所指向的数组是相同的:

1
2
3
4
5
>>> var b = a.sort();
>>> b
[1,3,5,7,"test"]
>>> a
[1,3,5,7,"test"]

join()方法会返回一个由目标数组中所有元素值连接而成的字符串,另外,我们还可以通过该方法的参数来设定这些元素之间的字符串。例如:

1
2
>>> a.join(' is not');
"1 is not 3 is not 5 is not 7 is not test"

slice() 方法会在不修改目标数组的情况下返回其中的某个片段,该片段的首尾索引位置由slice()的头两个参数来指定(都以0为基数)。

1
2
3
4
5
6
>>> b = a.slice(1,3);
[3,5]
>>> b = a.slice(0,1);
[1]
>>> b = a.slice(0,2);
[1,3]

所有的截取完成之后,原数组的状态不变:

1
2
>>> a
[1,3,5,7,"test"]

splice() 则是会修改目标数组的,它会移除并返回指定切片,并且在可选情况下,它还会用指定的新元素来填补被切除的空缺。该方法的头两个参数所指定的是要移除切片的首尾索引位置,其他参数则是用于填补新元素的值。

1
2
3
4
>>> b = a.splice(1,2,100,101,102);
[3,5]
>>> a
[1,100,101,102,7,"test"]

当然,用于填补空缺的新元素是可选的,我们也可以直接跳过:

1
2
3
4
>>> a.splice(1,3)
[100,101,102]
>>> a
[1,7,"test"]

Function

之前,我们已经了解了函数是一种特殊的数据类型,但事实还远远不止如此,它实际上是一种对象,函数对象的內建构造器是Function(),我们可以将它作为创建函数的一种备选方式(但我门不推荐这种方式)
也就是说下面三种定义函数的方式都是等效的:

1
2
3
4
5
6
7
8
9
>>> function sum(a,b) {return a+b};
>>> sum(1,2)
3
>>> var sum = function(a,b) {return a+b};
>>> sum(1,2);
3
>>> var sum = new Function('a','b','return a+b;');
>>> sum(1,2)
3

热评文章