JavaSript对象
简介
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
在Javascript中, 几乎所有的事务都是对象, 对象也可以是一个变量,但是可以包括多个值, 它是以name:value
的形式存在的(可以说对象是变量的容器, 封装了属性和方法)
var car = {name:value
}
// 这种形式和python的字典相像
在javascript中的对象分为三种:
- 自定义对象
- 内置对象(如date,math,array等等)
- 浏览器对象(如Window对象,Document对象,History对象等)
创建对象的方法
一, 采用name:value形式创建
var star = {name : 'xxxx',age : 'xx',sex : 'xx',fun : function(){alert('xxxx');}
};typeof this.star //=> 'object'
二, 利用new object创建对象
var star = new Object();
star.name = 'xxxx';
star.age = 'xx';
star.sex = 'xx';
star.fun = function(){alert('xxxx');
}console.log(typeof star); //=> Object
三, 利用构造函数创建对象
var star = function(){this.name = 'xxxx',this.age = 'xx',this.sex = 'x',this.fun = function(){alert('xxxx');}
};var p = new star();
console.log(typeof p);
对象的相关方法
Object.getPrototypeOf()
Object.getPrototypeOf
方法返回参数对象的原型。这是获取原型对象的标准方法。
var star = function(){this.name = 'xxxx',this.age = 'xx',this.sex = 'x',this.fun = function(){alert('xxxx');}
};var p = new star();
Object.getPrototypeOf(p) === star.prototype //true
Object.setPrototypeOf()
Object.setPrototypeOf
() 静态方法可以将一个指定对象的原型(即内部的 属性)设置为另一个对象或者 null
。
const obj = {};
const parent = { foo: 'bar' };console.log(obj.foo);
// Expected output: undefinedObject.setPrototypeOf(obj, parent);console.log(obj.foo);
// Expected output: "bar"
Object.create()
Object.create()
静态方法以一个现有对象作为原型,创建一个新对象
const person = {isHuman: false,printIntroduction: function() {console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);}
};const me = Object.create(person);me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // Inherited properties can be overwrittenme.printIntroduction();
// Expected output: "My name is Matthew. Am I human? true"
Object.prototype.isPrototypeOf()
实例对象的isPrototypeOf
方法,用来判断该对象是否为参数对象的原型。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);// o2 --> o1
// 03 --> o2 --> o1
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
// o1和o2都是o3的原型
Object.prototype.__proto__
function Circle() {}
const shape = {};
const circle = new Circle();// 设置该对象的原型
// 已弃用。这里只是举个例子,请不要在生产环境中这样做。
shape.__proto__ = circle;// 判断该对象的原型链引用是否属于 circle
console.log(shape.__proto__ === circle); // true
获取原型对象方法的比较
如前所述,__proto__
属性指向当前对象的原型对象,即构造函数的prototype
属性。
var obj = new Object();obj.__proto__ === Object.prototype
// true
obj.__proto__ === obj.constructor.prototype
// true
上面代码首先新建了一个对象obj
,它的__proto__
属性,指向构造函数(Object
或obj.constructor
)的prototype
属性。
因此,获取实例对象obj
的原型对象,有三种方法。
obj.__proto__
obj.constructor.prototype
Object.getPrototypeOf(obj)
上面三种方法之中,前两种都不是很可靠。__proto__
属性只有浏览器才需要部署,其他环境可以不部署。而obj.constructor.prototype
在手动改变原型对象时,可能会失效。
var P = function () {};
var p = new P();var C = function () {};
C.prototype = p;
var c = new C();c.constructor.prototype === p // false
上面代码中,构造函数C
的原型对象被改成了p
,但是实例对象的c.constructor.prototype
却没有指向p
。所以,在改变原型对象时,一般要同时设置constructor
属性。
C.prototype = p;
C.prototype.constructor = C;var c = new C();
c.constructor.prototype === p // true
因此,推荐使用第三种Object.getPrototypeOf
方法,获取原型对象。
Object.getOwnPropertyNames()
Object.getOwnPropertyNames()
静态方法返回一个数组,其包含给定对象中所有自有属性(包括不可枚举属性,但不包括使用 symbol 值作为名称的属性)。
const object1 = {a: 1,b: 2,c: 3
};console.log(Object.getOwnPropertyNames(object1));
// Expected output: Array ["a", "b", "c"]
Object.prototype.hasOwnProperty()
hasOwnProperty
() 方法返回一个布尔值,表示对象自有属性(而不是继承来的属性)中是否具有指定的属性。
const object1 = {};
object1.property1 = 42;console.log(object1.hasOwnProperty('property1'));
// Expected output: trueconsole.log(object1.hasOwnProperty('toString'));
// Expected output: falseconsole.log(object1.hasOwnProperty('hasOwnProperty'));
// Expected output: false
this关键字
作用域
JavaScript的作用域分以下三种:
- 全局作用域:脚本模式运行所有代码的默认作用域
- 模块作用域:模块模式中运行代码的作用域
- 函数作用域:由函数创建的作用域
- 块级作用域:用一对花括号(一个代码块)创建出来的作用域(用
let
或const
声明的变量属于额外的作用域)
绑定this的方法
Function.prototype.call()
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
var obj = {};var f = function () {return this;
};f() === window // true
f.call(obj) === obj // true
上面代码中,全局环境运行函数f
时,this
指向全局环境(浏览器为window
对象);call
方法可以改变this
的指向,指定this
指向对象obj
,然后在对象obj
的作用域中运行函数f
。
call
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
var n = 123;
var obj = { n: 456 };function a() {console.log(this.n);
}a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
上面代码中,a
函数中的this
关键字,如果指向全局对象,返回结果为123
。如果使用call
方法将this
关键字指向obj
对象,返回结果为456
。可以看到,如果call
方法没有参数,或者参数为null
或undefined
,则等同于指向全局对象。
如果call
方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call
方法。
var f = function () {return this;
};f.call(5)
// Number {[[PrimitiveValue]]: 5}
上面代码中,call
的参数为5
,不是对象,会被自动转成包装对象(Number
的实例),绑定f
内部的this
。
call
方法还可以接受多个参数。
func.call(thisValue, arg1, arg2, ...)
call
的第一个参数就是this
所要指向的那个对象,后面的参数则是函数调用时所需的参数。
function add(a, b) {return a + b;
}add.call(this, 1, 2) // 3
上面代码中,call
方法指定函数add
内部的this
绑定当前环境(对象),并且参数为1
和2
,因此函数add
运行后得到3
。
call
方法的一个应用是调用对象的原生方法。
var obj = {};
obj.hasOwnProperty('toString') // false// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {return true;
};
obj.hasOwnProperty('toString') // trueObject.prototype.hasOwnProperty.call(obj, 'toString') // false
上面代码中,hasOwnProperty
是obj
对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。call
方法可以解决这个问题,它将hasOwnProperty
方法的原始定义放到obj
对象上执行,这样无论obj
上有没有同名方法,都不会影响结果。
Function.prototype.apply()
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
apply
方法的第一个参数也是this
所要指向的那个对象,如果设为null
或undefined
,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call
方法中必须一个个添加,但是在apply
方法中,必须以数组形式添加。
function f(x, y){console.log(x + y);
}f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
上面代码中,f
函数本来接受两个参数,使用apply
方法以后,就变成可以接受一个数组作为参数。
Function.prototype.bind()
bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
var d = new Date();
d.getTime() // 1481869925657var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.
上面代码中,我们将d.getTime()
方法赋给变量print
,然后调用print()
就报错了。这是因为getTime()
方法内部的this
,绑定Date
对象的实例,赋给变量print
以后,内部的this
已经不指向Date
对象的实例了。
bind()
方法可以解决这个问题。
var print = d.getTime.bind(d);
print() // 1481869925657
上面代码中,bind()
方法将getTime()
方法内部的this
绑定到d
对象,这时就可以安全地将这个方法赋值给其他变量了。
bind
方法的参数就是所要绑定this
的对象,下面是一个更清晰的例子。
var counter = {count: 0,inc: function () {this.count++;}
};var func = counter.inc.bind(counter);
func();
counter.count // 1
上面代码中,counter.inc()
方法被赋值给变量func
。这时必须用bind()
方法将inc()
内部的this
,绑定到counter
,否则就会出错。
this
绑定到其他对象也是可以的。
var counter = {count: 0,inc: function () {this.count++;}
};var obj = {count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
上面代码中,bind()
方法将inc()
方法内部的this
,绑定到obj
对象。结果调用func
函数以后,递增的就是obj
内部的count
属性。
bind()
还可以接受更多的参数,将这些参数绑定原函数的参数。
var add = function (x, y) {return x * this.m + y * this.n;
}var obj = {m: 2,n: 2
};var newAdd = add.bind(obj, 5);
newAdd(5) // 20
上面代码中,bind()
方法除了绑定this
对象,还将add()
函数的第一个参数x
绑定成5
,然后返回一个新函数newAdd()
,这个函数只要再接受一个参数y
就能运行了。
如果bind()
方法的第一个参数是null
或undefined
,等于将this
绑定到全局对象,函数运行时this
指向顶层对象(浏览器为window
)。
function add(x, y) {return x + y;
}var plus5 = add.bind(null, 5);
plus5(10) // 15
上面代码中,函数add()
内部并没有this
,使用bind()
方法的主要目的是绑定参数x
,以后每次运行新函数plus5()
,就只需要提供另一个参数y
就够了。而且因为add()
内部没有this
,所以bind()
的第一个参数是null
,不过这里如果是其他对象,也没有影响。