阮一峰的JavaScript 教程读书笔记之面向对象编程

OOP

实例对象与new命令

构造函数

JS中的面向对象是通过构造函数(constructor)和原型链(prototype)来实现的。JavaScript 语言使用构造函数(constructor)作为对象的模板。

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

为了与普通函数区别,构造函数名字的第一个字母通常大写。

  • 函数体内使用this关键词代表了所要生成的对象实例
  • 生成对象的时候,必须使用new命令

new命令

new 命令就是生成一个实例对象。

var v = new Vehicle();
v.price // 1000

new命令执行时,构造函数内部的this,就代表了新生成的实例对象,this.price表示实例对象有一个price属性,值是1000。

可以通过在构造函数第一行加上use strict以防止不使用new,直接调用函数,此时会报错。同时也可以使用new.target属性来判断是否通过new命令调用构造函数。

function f() {
   
  if (!new.target) {
   
    throw new Error('请使用 new 命令调用!');
  }
  // ...
}

f() // Uncaught Error: 请使用 new 命令调用!

new 命令的原理
(1)创建一个空对象,作为要返回的对象实例
(2)将这个空对象的原型,指向构造函数的prototype属性
(3)将这个空对象赋值给函数内部的this关键词
(4)继续执行构造函数内部操作

构造函数之所以叫构造函数,是说这个函数的目的是操作空对象,构造成需要的样子。

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

//返回this对象的例子:
var Vehicle = function () {
   
  this.price = 1000;
  return 1000;
};

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

//返回对象的例子:
var Vehicle = function (){
   
  this.price = 1000;
  return {
    price: 2000 };
};

(new Vehicle()).price
// 2000

有时候我们拿不到构造函数,可以使用Object.create方法来创建实例对象,person2继承了person1的方法和属性。

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

var person2 = Object.create(person1);

person2.name // 张三
person2.greeting() // Hi! I'm 张三.

this 关键字

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

由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,this的指向是可变的

var A = {
   
  name: '张三',
  describe: function () {
   
    return '姓名:'+ this.name;
  }
};

var B = {
   
  name: '李四'
};

B.describe = A.describe;
B.describe()
// "姓名:李四"

上面代码中,A.describe属性被赋给B,于是B.describe就表示describe方法所在的当前对象是B,所以this.name就指向B.name

只要函数赋值给另一个变量,this指向就会变。下述例子中,A.describe被赋值给f后,this.name就会选择f运行时所在的name

var A = {
   
  name: '张三',
  describe: function () {
   
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

总结一下,JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。

实质

看似简单的一句代码,其实JS引擎会在内存中存储一个对象{foo:5},然后让obj指向该内存地址(或者说obj就是一个地址),所以当访问obj.foo时,会首先从内存地址找到那个对象,然后再去拿他的foo属性。

var obj = {
    foo:  5 };

在内存中该对象的存储形式为字典形式。

{
   
  foo: {
   
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}

当对象中属性不是一个值而是一个函数时,会将函数的地址存储到value中。

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

而函数可能在不同的环境下执行。

var f = function () {
   };
var obj = {
    f: f };

// 单独执行
f()

// obj 环境执行
obj.f()

JavaScript 允许在函数体内部,引用当前环境的其他变量。比如下列函数中的x变量,允许由环境来提供。

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

设计this的原因,是要能够在函数体内部获得当前的运行环境(找到调用函数所在的当前环境)。

因此在f函数中会使用this来指代当前的运行环境。下面为一个典型的例子,如果单独执行的话,属于在最外层运行环境,因此x是取全局变量1;而如果是在obj环境执行,则x是取局部变量2。

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

var x = 1;
var obj = {
   
  f: f,
  x: 2,
};

// 单独执行
f() // 1

// obj 环境执行
obj.f() // 2

使用场合

(1)全局变量

全局环境使用this,它指的就是顶层对象window。

this === window // true

function f() {
   
  console.log(this === window);
}
f() // true
(2)构造函数

构造函数中使用this,它指的是实例对象。

var Obj = function (p) {
   
  this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
(3)对象的方法

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

这个很难理解,先看下面的例子,此时调用结果是obj,因为方法运行时所在的对象或环境就是obj。

var obj ={
   
  foo: function () {
   
    console.log(this);
  }
};

obj.foo() // obj

而接下来三种情况都是最外层的window,可以这样理解:obj.foo()相当于是从obj的地址去调用foo的地址,所以运行环境是在obj,因此this指向obj。
而下述三种情况都是直接去调用的foo,可以理解为直接从window调用的,所以this就指向window了。

// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值