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,不过这里如果是其他对象,也没有影响。

prototype属性

原型链

在JavaScript中, 所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOftoString方法的原因,因为这是从Object.prototype继承的。

prototype 属性介绍

在Javascript中,每个函数都有一个prototype属性指向一个对象

function f() {
    
    };
typeof f.prototype //=>'object'

// 函数f默认具有prototype属性,指向一个对象。

对于构造函数来说, 生成实例的时候, 该属性会自动成为实例对象的原型。

function Anmal(name){
    
    
    this.name = name;
};

var name1 = new Anmal('大猫');
var name2 = new Anmal('小猫');

// 由上述代码可以知道name1,name2的原型对象是Anmal

name1.prototype.color = 'white';
上面代码中,`name1.prototype`对象上面定义了一个`color`属性,这个属性将可以在所有`name1`实例对象上面调用。

constructor属性

描述

除了 null 原型对象之外,任何对象都会在其 [[Prototype]] 上有一个 constructor 属性。使用字面量创建的对象也会有一个指向该对象构造函数类型的 constructor 属性,例如,数组字面量创建的 Array 对象和对象字面量创建的普通对象。

例如:

function Tree(name) {
    
    
  this.name = name;
}

const theTree = new Tree("Redwood");
console.log(`theTree.constructor 是 ${
      
      theTree.constructor}`);

// 输出为
theTree.constructor 是 function Tree(name) {
    
    
  this.name = name;
}

___proto___属性

描述

__proto__ 的 getter 函数暴露了一个对象内部的 [[Prototype]] 的值。对于使用对象字面量创建的对象,该值是 Object.prototype。对于使用数组字面量创建的对象,该值是 Array.prototype。对于函数,该值是 Function.prototype。你可以在继承与原型链中了解有关原型链的更多信息。

__proto__ 的 setter 允许修改一个对象的 [[Prototype]]。提供的值必须是一个对象或 null。提供任何其他值都不会产生任何作用。

Object.getPrototypeOf()Object.setPrototypeOf() 不同,它们始终作为 Object 的静态属性存在并始终反映 [[Prototype]] 内部属性。__proto__ 属性并不总是作为所有对象的属性存在,因此不能可靠地反映 [[Prototype]]

__proto__ 属性是 Object.prototype 上一个简单的访问器属性,由 getter 和 setter 函数组成。如果访问 __proto__ 属性时最终查询到 Object.prototype,则会找到该属性,但如果没有查询 Object.prototype,则不会找到该属性。如果在查询 Object.prototype 之前就找到了其他的 __proto__ 属性,则会覆盖 Object.prototype 上的 __proto__ 属性。

null 原型对象不从 Object.prototype 继承任何属性,包括 __proto__ 访问器属性。因此,如果你尝试在这样的对象上读取 __proto__,则无论对象的实际 [[Prototype]] 是什么,该值都将始终为 undefined,并且对 __proto__ 的任何赋值都将创建一个名为 __proto__ 的新属性,而不是设置对象的原型。此外,__proto__ 可以通过 Object.defineProperty() 作为任何对象实例的自有属性重新定义,而不触发 setter。在这种情况下,__proto__ 将不再是 [[Prototype]] 的访问器。因此,为设置和获取对象的 [[Prototype]],请始终使用 Object.getPrototypeOf()Object.setPrototypeOf()

const ShapeA = function () {
    
    };
const ShapeB = {
    
    
  a() {
    
    
    console.log("aaa");
  },
};

ShapeA.prototype.__proto__ = ShapeB;
console.log(ShapeA.prototype.__proto__); // { a: [Function: a] }

const shapeA = new ShapeA();
shapeA.a(); // aaa
console.log(ShapeA.prototype === shapeA.__proto__); // true

猜你喜欢

转载自blog.csdn.net/bo1029/article/details/131970975