JavaScript 进阶营

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

2017-12-24  本文已影响253人  woow_wu7

(1) 对象

(2) 构造函数

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

(二)构造函数的写法就是一个普通的函数,但是有自己的特征和用法。


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对象。


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命令的作用,就是执行构造函数,返回一个实例对象。


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

var v = new Vehicle(500);



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


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

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


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。


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添加属性)。

(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)对象。

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


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.getPrototypeOf(Object.prototype)
// null



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

(4) constructor 属性

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



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。



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间接调用构造函数。





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

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




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


f.constructor.name // "Foo"


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

(5) instanceof 运算符

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


var v = new Vehicle();

v instanceof Vehicle          // true            


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









Object 对象与继承

(1) Object.getOwnPropertyNames()

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

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

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


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 循环


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


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

上一篇下一篇

猜你喜欢

热点阅读