JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。
它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。
new命令:
new命令的作用,就是执行构造函数,返回一个实例对象。
var Vehicle = function (p) {
  this.price = p;
};
var v = new Vehicle(500);
new 命令的原理
使用new命令时,它后面的函数依次执行下面的步骤。 1.创建一个空对象,作为将要返回的对象实例。 2.将这个空对象的原型,指向构造函数的prototype属性。 3.将这个空对象赋值给函数内部的this关键字。 4.开始执行构造函数内部的代码。
也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。
构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。
如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。
var Vehicle = function () {
  this.price = 1000;
  return 1000;
};
(new Vehicle()) === 1000
// false
上面代码中,构造函数Vehicle的return语句返回一个数值。这时,new命令就会忽略这个return语句,返回“构造”后的this对象。
但是,如果return语句返回的是一个跟this无关的新对象,new命令会返回这个新对象,而不是this对象。这一点需要特别引起注意。
var Vehicle = function (){
  this.price = 1000;
  return { price: 2000 };
};
(new Vehicle()).price
// 2000
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() // Hi! I'm 张三.
对象person1是person2的模板,后者继承了前者的属性和方法。
this关键字
this可以用在构造函数之中,表示实例对象。除此之外,this还可以用在别的场合。但不管是什么场合,this都有一个共同点:它总是返回一个对象。
this就是属性或方法“当前”所在的对象。
this.propertythis就代表property属性当前所在的对象。
只要函数被赋给另一个变量,this的指向就会变。
var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};
var name = '李四';
var f = A.describe;
f() // "姓名:李四"
内部的this就会指向f运行时所在的对象(本例是顶层对象)。
this
JavaScript 语言之所以有 this 的设计,目的就是在函数体内部,指代函数当前的运行环境。
this主要有以下几个使用场合。
1、全局环境使用this,它指的就是顶层对象window。
this === window // true
function f() {
  console.log(this === window);
}
f() // true
上面代码说明,不管是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window。
2、构造函数中的this,指的是实例对象。
var Obj = function (p) {
  this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
上面代码定义了一个构造函数Obj。由于this指向实例对象,所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性。
3、对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
var obj ={
  foo: function () {
    console.log(this);
  }
};
obj.foo() // obj
obj.foo方法执行时,它内部的this指向obj。
下面这几种用法,都会改变this的指向。
// 情况一 (obj.foo = obj.foo)() // window // 情况二 (false || obj.foo)() // window // 情况三 (1, obj.foo)() // window 上面代码中,obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj。 可以这样理解,JavaScript 引擎内部,obj和obj.foo储存在两个内存地址,称为地址一和地址二。obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj。
但是,上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。上面三种情况等同于下面的代码。 // 情况一 (obj.foo = function () { console.log(this); })() // 等同于 (function () { console.log(this); })() // 情况二 (false || function () { console.log(this); })() // 情况三 (1, function () { console.log(this); })()
如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
var a = { p: 'Hello', b: { m: function() { console.log(this.p); } } }; a.b.m() // undefined a.b.m方法在a对象的第二层,该方法内部的this不是指向a,而是指向a.b
如果这时将嵌套对象内部的方法赋值给一个变量,this依然会指向全局对象。
var a = { b: { m: function() { console.log(this.p); }, p: 'Hello' } }; var hello = a.b.m; hello() // undefined
数组的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()
foreach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而指向顶层对象。
解决这个问题的一种方法,就是前面提到的,使用中间变量固定this。
var o = { v: 'hello', p: [ 'a1', 'a2' ], f: function f() { var that = this; this.p.forEach(function (item) { console.log(that.v+' '+item); }); } } o.f() // hello a1 // hello a2
另一种方法是将this当作foreach方法的第二个参数,固定它的运行环境。
var o = { v: 'hello', p: [ 'a1', 'a2' ], f: function f() { this.p.forEach(function (item) { console.log(this.v + ' ' + item); }, this); } } o.f() // hello a1 // hello a2
绑定this的方法:
JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
1、call方法是改变this指向;也就是将外面的对象的this指向调用的()里面。
var n = 123; var obj = { n: 456 }; function a() { console.log(this.n); } a.call() // 123 ---- 指向全局对象 a.call(null) // 123 ---- 参数为null或undefined,则等同于指向全局对象。 a.call(undefined) // 123 a.call(window) // 123 a.call(obj) // 456 ---- 指向obj
call方法还可以接受多个参数。
call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。
func.call(thisValue, arg1, arg2, ...)
function add(a, b) {
  return a + b;
}
add.call(this, 1, 2) // 32、apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,
func.apply(thisValue, [arg1, arg2, ...])
使用apply方法和Math.max方法,就可以返回数组的最大元素。
var a = [10, 2, 4, 15, 9]; Math.max.apply(null, a) // 15
利用数组对象的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]
3、bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。
bind方法的参数就是所要绑定this的对象。
var counter = { count: 0, inc: function () { this.count++; } }; var obj = { count: 100 }; var func = counter.inc.bind(obj); // 方法指向obj,this.count++ 为101 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); ---- 绑定对象是obj,然后 x 默认值为5,此时在传入 y 就可以了。 newAdd(5) // 20
原型对象:原型对象的作用,就是定义所有实例对象共享的属性和方法。
原型链:对象到原型,再到原型的原型。
所有对象都有自己的原型对象;任何一个对象,都可以充当其他对象的原型。由于原型对象也是对象,所以它也有自己的原型。
 Object.prototype的原型是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。
什么叫做'覆盖'? 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”
如果让构造函数的prototype属性指向一个数组,就意味着实例对象可以调用数组方法。
var MyArray = function () {};
MyArray.prototype = new Array();
MyArray.prototype.constructor = MyArray;
var mine = new MyArray();
mine.push(1, 2, 3);
mine.length // 3
mine instanceof Array // true
instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
var v = new Vehicle(); v instanceof Vehicle // true
instanceof运算符只能用于对象,不适用原始类型的值。
var s = 'hello'; s instanceof String // false
// 此外,对于undefined和null,instanceof运算符总是返回falseundefined instanceof Object // false
null instanceof Object // false利用instanceof运算符,还可以巧妙地解决,调用构造函数时,忘了加new命令的问题。
function Fubar (foo, bar) {
  if (this instanceof Fubar) {   // 判断this是否是Fubar的实例,如果不是,就表明忘了加new命令。
    this._foo = foo;
    this._bar = bar;
  } else {
    return new Fubar(foo, bar);
  }
}
子类继承父类:Shape()父类构造函数,Rectangle()子类
// 父类构造函数 function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function (x, y) { this.x += x; this.y += y; console.info('Shape moved.'); };
我们需要让Rectangle构造函数继承Shape。
// 第一步,子类继承父类的实例 function Rectangle() { Shape.call(this); // 调用父类构造函数 } // 第二步,子类继承父类的原型 Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle;
Rectangle是子类的构造函数,this是子类的实例。在实例上调用父类的构造函数Shape,就会让子类实例具有父类实例的属性。
这句话的理解:https://segmentfault.com/q/1010000005721719
Shape.call(this); 这时的 this 指向Rectangle本身。 Shape里面的this 指向 Rectangle 里面的this。
Object 对象的相关方法:
1、Object.getPrototypeOf方法返回参数对象的原型。
var F = function () {}; var f = new F(); Object.getPrototypeOf(f) === F.prototype // true
2、Object.setPrototypeOf方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。
var a = {}; var b = {x: 1}; Object.setPrototypeOf(a, b); Object.getPrototypeOf(a) === b // true a.x // 1
// 对象a的原型,设置为对象b,因此a可以共享b的属性。
3、Object.create()方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。
// 原型对象 var A = { print: function () { console.log('hello'); } }; // 实例对象 var B = Object.create(A); Object.getPrototypeOf(B) === A // true B.print() // hello B.print === A.print // true
//Object.create()方法以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
 4、实例对象的__proto__属性(前后各两个下划线),返回该对象的原型。该属性可读写。
var obj = {}; var p = {}; obj.__proto__ = p; Object.getPrototypeOf(obj) === p // true
// 上面代码通过__proto__属性,将p对象设为obj对象的原型。
5、Object.getOwnPropertyNames方法返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名。
方法返回所有键名,不管是否可以遍历。只获取那些可以遍历的属性,使用Object.keys方法。
6、对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。
hasOwnProperty方法是 JavaScript 之中唯一一个处理对象属性时,不会遍历原型链的方法。
7、in运算符和for...in循环
in运算符返回一个布尔值,表示一个对象是否具有某个属性。in运算符常用于检查一个属性是否存在。
获得对象的所有可遍历属性(不管是自身的还是继承的),可以使用for...in循环。 
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号