美文网首页JavaScript 进阶营
React.js学习笔记(13) 面向对象编程 + ( 构造函数

React.js学习笔记(13) 面向对象编程 + ( 构造函数

作者: woow_wu7 | 来源:发表于2017-12-24 23:52 被阅读253次

(1) 对象

  • 对象是单个实物的抽象。
  • 对象是一个容器,封装了属性(property)和方法(method),属性是对象的状态,方法是对象的行为(完成某种任务)。

(2) 构造函数

(一)JavaScript 语言的对象体系,是基于构造函数(constructor)和原型链(prototype)。

  • 所谓”构造函数”,就是专门用来生成对象的函数。
  • 构造函数提供模板,描述对象的基本结构。
  • 一个构造函数,可以生成多个对象,这些对象都有相同的结构。
(二)构造函数的写法就是一个普通的函数,但是有自己的特征和用法。
  • 为了与普通函数区别,构造函数名字的第一个字母通常大写。
  • 构造函数的特点:
    函数体内部使用了 this 关键字,代表了所要生成的对象实例。
    生成对象的时候,必需用 new 命令,调用构造函数。


var Vehicle = function () {   // 构造函数首字母要大写,来区分普通函数
  'use strict';               // 严格模式,保证调用构造函数时,忘记加new命令,就会报错
  this.price = 1000;          // this代表所要生成的实例对象
};
var v = new Vehicle();        // new命令,执行构造函数Vehicle,返回一个实例对象,保存在变量v中。
// var v = new Vehicle;       // new命令本身就可以执行构造函数,所以可以带括号,也可以不带括号。
v.price // 1000               // v继承了构造函数Vehicle中的price属性



------

上面代码中,Vehicle (交通工具的意思) 就是构造函数,它提供模板,用来生成实例对象。

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

------

上面代码通过new命令,让构造函数Vehicle生成一个实例对象,保存在变量v中。

这个新生成的实例对象,从构造函数Vehicle继承了price属性。

new命令执行时,构造函数内部的this,就代表了新生成的实例对象

this.price表示实例对象有一个price属性,值是1000。


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

  • 构造函数:new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。
  • 普通函数:return后跟一个对象,new命令会返回return指定的对象,return后跟的不是对象(如:return 'xxxxx';字符串),new命令返回一个空对象

var Vehicle = function () {
  this.price = 1000;
  return 1000;     // 构造函数内部,return后面如果跟一个对象,就会返回return指定的对象
};                 // 如果构造函数内部return后不是跟的对象,就会忽略这个return语句,返回this对象。

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


上面代码中,构造函数Vehicle的return语句返回一个数值。

这时,new命令就会忽略这个return语句,返回“构造”后的this对象。




---------------------------------------------------------------------------------

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

(new Vehicle()).price
// 2000


上面代码中,构造函数Vehicle的return语句,返回的是一个新对象。new命令会返回这个对象,而不是this对象

(四)如果对普通函数(内部没有this关键字的函数)使用new命令,如果return后跟的不是一个对象,则会返回一个空对象。
(return 后跟的是对象,还是会返回return指定的对象)


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

var msg = new getMessage();   // 普通函数使用new命令,会返回一个空对象
                              // new命令总是要返回一个对象,实例对象或者return指定的对象
msg // {}
typeof msg // "object"


上面代码中,getMessage是一个普通函数,返回一个字符串。对它使用new命令,会得到一个空对象。


这是因为new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。

本例中,return语句返回的是字符串,所以new命令就忽略了该语句。

构造函数中return的不同情况

(3) new 命令

new命令的作用,就是执行构造函数,返回一个实例对象。

  • 使用new命令时,根据需要,构造函数也可以接受参数

var Vehicle = function (p) {       // 构造函数也可以接受参数。
  this.price = p;
};

var v = new Vehicle(500);



-----------------------------------------------


new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。下面两行代码是等价的。

var v = new Vehicle();
var v = new Vehicle;

  • 如果忘了使用new命令,直接调用构造函数,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,this这时代表全局对象,将造成一些意想不到的结果。

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

var v = Vehicle();
v.price
// Uncaught TypeError: Cannot read property 'price' of undefined

price
// 1000



上面代码中,调用Vehicle构造函数时,忘了加new命令。

导致,price属性变成了全局变量,而变量v变成了undefined。

  • 为了保证构造函数必须与new命令一起使用,在构造函数内部使用严格模式,即第一行加上use strict

function Fubar(foo, bar){
  'use strict';         // 严格模式中,函数内部的this不能指向全局对象,默认等于undefined
  this._foo = foo;
  this._bar = bar;
}

Fubar()
// TypeError: Cannot set property '_foo' of undefined



上面代码的Fubar为构造函数,use strict命令保证了该函数在严格模式下运行。

由于在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,

导致不加new调用会报错(JavaScript 不允许对undefined添加属性)。

  • 如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。
  • 如果return语句返回的是一个跟this无关的新对象,new命令会返回这个新对象,而不是this对象。这一点需要特别引起注意。
  • 如果对普通函数(内部没有this关键字的函数)使用new命令,return后如果跟的不是对象,则会返回一个空对象。

(4) new.target

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

(5) Object.create() 创建实例对象

构造函数作为模板,可以生成实例对象。但是,有时只能拿到实例对象,而该对象根本就不是由构造函数生成的,这时可以使用Object.create()方法,直接以某个实例对象作为模板,生成一个新的实例对象。


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

var person2 = Object.create(person1);         // 以person1对象为模板,生成person2 对象,相当于复制

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



上面代码中,对象person1是person2的模板,后者继承了前者的属性和方法。









prototype 对象

(1) 构造函数的缺点

同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。

  • 下面的例子(重要):

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.meow = function () {
    console.log('喵喵');
  };
}

var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');

cat1.meow === cat2.meow                
// false



上面代码中,cat1和cat2是同一个构造函数的两个实例,它们都具有meow方法。


由于meow方法是生成在每个实例对象上面,所以两个实例就生成了两次。( 重要 )

也就是说,每新建一个实例,就会新建一个meow方法。( 重要 )


这既没有必要,又浪费系统资源,因为所有meow方法都是同样的行为,完全应该共享。

这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。


(2) prototype 属性的作用

JavaScript 的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。

  • 一方面,任何一个对象,都可以充当其他对象的原型;
  • 另一方面,由于原型对象也是对象,所以它也有自己的原型。
  • null也可以充当原型,区别在于它没有自己的原型对象
  • JavaScript 继承机制的设计就是,原型的所有属性和方法,都能被子对象共享。( 重要 )
  • 每一个构造函数都有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象。( 重要 )
  • 原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
  • 当实例对象本身没有某个属性或方法的时候,它会到构造函数的prototype属性指向的对象,去寻找该属性或方法。这就是原型对象的特殊之处。
  • 如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
  • 构造函数也是普通的函数, 所以实际上所有函数都有prototype属性。

总结:
原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。


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

Animal.prototype.color = 'white';       
// 每一个构造函数都有prototype属性,在生成实例的时候成为实例对象的原型对象


var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');

cat1.color // 'white'      // 实例对象继承了原型对象(prototype)的color属性
cat2.color // 'white'



上面代码中,构造函数Animal的prototype对象,就是实例对象cat1和cat2的原型对象。

原型对象上添加一个color属性,结果,实例对象都继承了该属性。

(3) 原型链

对象的属性和方法,有可能定义在自身,也有可能定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。

  • 如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。
  • Object.prototype对象的原型对象是null对象,而null对象没有自己的原型。
  • Object.getPrototypeOf(object)方法返回指定对象的原型
  • “原型链”的作用是,读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
  • 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)
  • 性能影响:在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。


Object.getPrototypeOf(Object.prototype)
// null



Object.getPrototypeOf(object)方法返回指定对象的原型

(4) constructor 属性

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

  • 由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。
  • constructor属性的作用,是分辨原型对象到底属于哪个构造函数。
  • 有了constructor属性,就可以从实例新建另一个实例。 ( 重要 )
  • 通过name属性,可以从实例得到构造函数的名称。


function P() {}
var p = new P();    

p.constructor                 // 实例对象继承了构造函数的prototype属性的constructor属性
// function P() {}

p.constructor === P.prototype.constructor    // P.prototype.constructor指向prototype对象的构造函数
// true                                      // 即 P.prototype.constructor 指向 大P

p.hasOwnProperty('constructor')      // constructor不是实例对象上的属性
// false




----------------------------------------------------------------------------


function F() {};
var f = new F();

f.constructor === F // true
f.constructor === RegExp // false


上面代码表示,使用constructor属性,确定实例对象f的构造函数是F,而不是RegExp。


  • 有了constructor属性,就可以从实例新建另一个实例。( 重要 )

function Constr() {}
var x = new Constr();

var y = new x.constructor();       // x.constructor == Constr(){}  相当于  var y = new Constr();
y instanceof Constr // true


上面代码中,x是构造函数Constr的实例,可以从x.constructor间接调用构造函数。





------------------------------------------------------------------------------------

这使得在实例方法中,调用自身的构造函数成为可能。



  • 通过name属性,可以从实例得到构造函数的名称。

function Foo() {}
var f = new Foo();


f.constructor.name // "Foo"


prototype对象上constructor属性指向prototype所在的构造函数

(5) instanceof 运算符

instanceof运算符返回一个布尔值,表示某个对象是否为指定的构造函数的实例。
( instance是实例的意思 )

  • instanceof运算符的左边是实例对象,右边是构造函数

var v = new Vehicle();

v instanceof Vehicle          // true            


// instanceof 返回布尔值,表示对象是否是构造函数的实例;









Object 对象与继承

(1) Object.getOwnPropertyNames()

  • Object.getOwnPropertyNames方法返回一个数组,成员是对象本身的所有属性的键名,不包含继承的属性键名。
  • Object.getOwnPropertyNames方法返回所有键名。可以枚举的(enumerable)和 不可以枚举的
  • 只获取那些可以枚举的属性,使用Object.keys方法。

(2) Object.prototype.hasOwnProperty()-----自身属性

( in运算符不会区分该属性是对象自身的属性,还是继承的属性 )

对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

  • hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。

var name = {
    '1':10,
    '2':20
}

name.hasOwnProperty('1');   // true


// 因为hasOwnProperty是Object.prototype对象上的属性,所以被所有实例对象继承。

//实例对象可以直接使用hasOwnProperty


----------------------------

Date.hasOwnProperty('length')    // true

Date.hasOwnProperty('toString')  // false

(3) in 运算符和 for…in 循环

  • in运算符返回一个布尔值,表示一个对象是否具有某个属性。它不区分该属性是对象自身的属性,还是继承的属性。

'length' in Date // true
'toString' in Date // true

  • 为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。

for ( var name in object ) {
  if ( object.hasOwnProperty(name) ) {
    /* loop code */
  }
}

相关文章

网友评论

    本文标题:React.js学习笔记(13) 面向对象编程 + ( 构造函数

    本文链接:https://www.haomeiwen.com/subject/texhgxtx.html