js对象

实例对象与new命令

什么是对象

面向对象OOP是目前主流的编程范式,它将真实世界的复杂关系抽象为一个对象,然后由对象之间的分工合作完成对真实世界的模拟。

  1. 对象是单个实物的抽象
  2. 对象是一个容器,封装了属性和方法

构造函数

OOP编程首先生成对象。对于传统的面向对象语言C++和java,他们都有的概念,通过类来生成对象。而在js中却不是这样基于类的,它是基于构造函数(constructor)和原型链(prototype).

js中使用构造函数作为对象的模版,描述实例对象的基本看结构. 一个构造函数可以生成多个实例对象,这些实例对象都有相同的结构.

构造函数仅仅是一个普通的函数,有自己的特征和用法

var Vec = function(){
	this.price = 1000;
};

上述代码中的Vec函数既是一个构造函数, 一般习惯上将构造函数的第一个字母大写.

构造函数的特征点有两个

- 函数内部的`this	`关键字,代表了所要生产的对象实例
- 生成对象时候必须使用`new`命令

new命令

new 命令用于执行构造

	var Vec = function(){
		this.price = 1000;
	};
	var v = new Vec();
	v.price //1000

构造函数也可以接受参数

	var Vehicle = function (p) {
  		this.price = p;
	};

	var v = new Vehicle(500);

如果忘记使用new,那么构造变成了普通的函数调用,而构造函数中的this``指代全局对象, 所以在构造函数时忘记使用new` 将是一件很危险的事情.

*** 为了保证构造函数必须与new命令一起使用, 我们可以使用严格模式,在改模式下忘记使用new 编译器会报错. ***

	function Funca(foo,bar){
		'use strict';
		this._foo = foo;
		this._bar = bar;
	}

	var fua = Funca(); // 报错

另一个解决办法,构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。

function Fubar(foo, bar) {
  if (!(this instanceof Fubar)) {
    return new Fubar(foo, bar);
  }

  this._foo = foo;
  this._bar = bar;
}

Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1

new命令的原理

new命令会执行一下步骤.

1. 创建一个空对象,作为将要返回对象的实例.
2. 将这个空对象的原型, 指向构造函数prototype属性
3. 将这个空对象赋值给函数内部的`this`指针
4. 开始执行构造函数内部的代码

如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。

var Vehicle = function () {
  this.price = 1000;
  return 1000; // 在使用该构造时会忽略掉改语句,返回`this`
};

(new Vehicle()) === 1000
// false

如果对普通函数(内部没有this关键字的函数)使用new命令,则会返回一个空对象。

function getMessage() {
  return 'this is a message';
}

var msg = new getMessage();

msg // {}
typeof msg // "object"

new.target

函数内部可以使用new.target 属性. 如果当前函数是new命令调用,new.target指向当前函数,否则为undefined

Object.create() 创建实例对象

构造函数作为模版生成实例对象. 我们可以根据现有对象来创建一个对象.

var person1 = {
	name:'张三' ,
	age:38,
	greeting: function() {
		console.log('Hi! I\'m ' + this.name+ '.');
	}
};

var person2 = Object.create(person1);

person2.name
person2.greeting()

this 关键字

含义

简单说,this就是属性或方法“当前”所在的对象。

this.property
// 当前对象的property属性

js语言之中,一切皆为对象,运行环境也是对象,所以函数都是在某个对象中运行,this就是函数运行是所在的对象. js中this的指向是动态的,没有办法事先确定到底指向哪个对象.

实质

js中原始对象是以字典结构保存,每一个属性名都对应一个属性描述对象.

var obj = { foo: 5};
//以上对象中的foo属性实际上是以下面的形式保存
{
	foo: {
		[[value]]:5
		[[writable]]:true
		[[enumberable]]:true
		[[configurable]]:true
	}
}

当属性的值为函数时,引擎会将函数单独保存在内存中,然后将函数的地址赋值给foo属性的value属性.


{
	foo : {
		[[value]]: 函数地址
		...
	}
}

由于函数是一个单独的值,所以它可以在不同环境执行. 如果想要执行当前环境中的变量,那么需要使用this关键字:

var f = function (){
	console.log(this.x);
};

使用场合

全局环境

全局使用this, 他指的是顶层对象window .

this === window // true

function f() {
	console.log(this === window);
}
f(); //true

构造函数

构造函数中的this 指的是实例对象

var Obj = function(p) {
	this.p = p;
};

对象的方法

如果对象的方法里包含this,this的指向就是方法运行时所在的对象. 该方法给另一个对象就会改变this的指向.

使用注意点

避免多层this

避免数组处理方法中的this

数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}

o.f()
// undefined a1
// undefined a2

foreach方法的回调函数中的this指代的是window对象. ***内层this不指向外层,而是指向顶层的window对象.

解决方法有两种

  1. 使用中间变量固定
  2. 将this当做foreach的第二个参数.

避免回调函数中使用this

回调函数中的this往往会改变指向, 最好避免使用.

var o = new Object() ;
o.f = function (){
	console.log(this === o);
}

// jQuery的写法
$('#button').on('click', o.f);

点击按钮以后,控制台会显示false. 原因是此时this不再指向o 对象, 而是指向按钮对象. ***为了解决这个问题,可以对this进行绑定.

绑定this的方法

this的动态切换, 固然为Javasscript创造了灵活性. 有时, 需要把this固定下来, 避免出现意想不到的情况.
js中提供了call , apply, bind 这三个方法,来固定和切换this指针.

Function.prototype.call()

函数实例的call``方法,可以指定函数内部的this`的指向 (即函数执行时所在的域),然后在指定的作用于中调用改函数

var obj = {};
var f = function (){
	return this;
};
f() === window //true
f.call(obj) === obj //true

上述代码中,call方法可以改变this的指向为obj, 然后在对象obj的作用域中运行函数f

call 方法的参数应该是一个对象,如果为空, nullundefined , 则默认传入全局对象.

call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数

call方法可以调用对象的原生方法

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

// 覆盖掉继承的hasOwnProperty()方法
obj.hasOwnProperty = function () {
	return true;
};
obj.hasOwnProperty('toString') // true
Object.protopype.hasOwnProperty.call(obj,'toString') //false
// 将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论`obj`对象上是否有同名方法,都不会影响结果.

Function.prototype.apply()

apply() 方法的作用与call方法类似, 也是改变this指向,然后再调用该函数. 他们的区别是,它接受一个数组作为函数的参数.

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

利用apply()函数的特性, 可以做一些有趣的事情.

找出数组中的最大元素
var a = [10,2,4,15,9];
Math.max.apply(null,a) // 15
将数组的空元素变成undefined

通过apply()方法,利用Array的构造函数将数组的空元素变成undefined .

Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]

空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined。

转换类似数组的对象

另外,利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。

Array.prototype.slice.apply({0:1, length:1})  // [1]
Array.prototype.slice.apply({0:1})  // []
Array.prototype.slice.apply({0:1, length:2})  // [1,undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]

上面代码的apply方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的。从上面代码可以看到,这个方法起作用的前提是,被处理的对象必须有length属性,以及相对应的数字键。

绑定回调函数的对象
var o = new Object();

o.f = function () {
  console.log(this === o);
}

var f = function (){
  o.f.apply(o);
  // 或者 o.f.call(o);
};

// jQuery 的写法
$('#button').on('click', f);

Function.prototype.bind()

bind方法用于将函数体内的this绑定到某个对象上, 然后返回一个新函数.

var d = new Date();
d.getTime()
var print = d.getTime;
print() //报错

上述代码报错是因为getTime方法内部的this绑定了Date对象实例, 赋给变量print以后, 内部的this已经不在指向Date实例了.

使用bind方法即可解决这种问题.

var print = d.getTime.bind(d);
print() //正常输出

this不仅可以绑定到原对象中,还可以绑定到其他对象中

var counter = {
	count: 0,
	inc: function() {
		this.count++;
	}
};

var obj = {
	count: 100;
}

var func = counter.inc.bind(obj); //将inc方法中的`this	绑定到obj方法
func();
obj,count // 101
bind方法需要注意
  1. 每一次返回一个新的函数
  2. 结合回调函数使用
  3. 结合call方法使用

参考>https://wangdoc.com/javascript/oop/this.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值