JavaScript对象和this

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: undefined

Object.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 overwritten

me.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__属性,指向构造函数(Objectobj.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: true

console.log(object1.hasOwnProperty('toString'));
// Expected output: false

console.log(object1.hasOwnProperty('hasOwnProperty'));
// Expected output: false

this关键字

作用域

JavaScript的作用域分以下三种:

  • 全局作用域:脚本模式运行所有代码的默认作用域
  • 模块作用域:模块模式中运行代码的作用域
  • 函数作用域:由函数创建的作用域
  • 块级作用域:用一对花括号(一个代码块)创建出来的作用域(用 letconst 声明的变量属于额外的作用域)

绑定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方法的参数,应该是一个对象。如果参数为空、nullundefined,则默认传入全局对象。

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方法没有参数,或者参数为nullundefined,则等同于指向全局对象。

如果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绑定当前环境(对象),并且参数为12,因此函数add运行后得到3

call方法的一个应用是调用对象的原生方法。

var obj = {};
obj.hasOwnProperty('toString') // false

// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
  return true;
};
obj.hasOwnProperty('toString') // true

Object.prototype.hasOwnProperty.call(obj, 'toString') // false

上面代码中,hasOwnPropertyobj对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。call方法可以解决这个问题,它将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论obj上有没有同名方法,都不会影响结果。

Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])

apply方法的第一个参数也是this所要指向的那个对象,如果设为nullundefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在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() // 1481869925657

var 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()方法的第一个参数是nullundefined,等于将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,不过这里如果是其他对象,也没有影响。

你可能感兴趣的:(javascript,开发语言,ecmascript)