对JavaScript对象的明白

Javascript

媒介:关于javascript我总有些疑问,是否是一门面向对象的言语,总结了良久:Javascript是一种基于对象(object-based)的言语,你碰到的一切东西险些都是对象。然则,它又不是一种真正的面向对象编程(OOP)言语,实在重要是由于它没有供应象笼统、继续、重载等有关面向对象言语的很多功用, 而是把别的言语所建立的庞杂对象一致起来,从而构成一个异常壮大的对象系统。 这类独特性称它为prototype-basedOO(基于原型的面向对象)。
(这是针对ES6之前的规范)

在这篇博客中我引用了阮一峰先生的教程:http://javascript.ruanyifeng.com/oop/basic.html 这个教程讲得非常细致,假如有系统进修的须要,发起跳过我的文章,直接看这里。

js面向对象编程

JavaScript 言语的对象系统,不是基于“类”的,而是基于组织函数(constructor)和原型链(prototype)。

  • 一、JavaScript 言语运用组织函数(constructor)作为对象的模板。

    var User = function () {

    this.age = 23;
    this.name = "wanghaoxin";

    };

这是一个组织函数。

组织函数的写法就是一个一般的函数,然则有自身的特性和用法。

  • 函数体内部运用了this关键字,代表了所要天生的对象实例。
  • 天生对象的时刻,必需用new敕令,挪用Vehicle函数。

    var user = new User();//这里的等同于 var user = new User;
    user.age = 123;
    console.log(user.age);
    console.log(user.name);

实行这段代码效果就是 123 、 wanghaoxin,这里的user.age经过了赋值,所以是赋值后的值。

由于js并非java一样的强范例言语,所以不能再编译前搜检你的语法错误,这里有个题目。

> 一个很天然的题目是,假如忘了运用new敕令,直接挪用组织函数会发作什么事?

这类状况下,组织函数就变成了一般函数,并不会天生实例对象。而且由于背面会说到的缘由,this这时候代表全局对象,将形成一些意想不到的效果。

  • 二、new敕令的道理

运用new敕令时,它背面的函数挪用就不是一般的挪用,而是顺次实行下面的步骤。

    1. 建立一个空对象,作为将要返回的对象实例. 
    2. 将这个空对象的原型,指向组织函数的prototype属性.
    3. 将这个空对象赋值给函数内部的this关键字.
    4. 最先实行组织函数内部的代码.

也就是说,组织函数内部,this指的是一个新天生的空对象,一切针对this的操纵,都邑发作在这个空对象上。组织函数之所以叫“组织函数”,就是说这个函数的目标,就是操纵一个空对象(即this对象),将其“组织”为须要的模样。

假如组织函数内部有return语句,而且return背面随着一个对象,new敕令会返回return语句指定的对象;不然,就会不论return语句,返回this对象。

  • 三、运用 Object.create() 建立实例对象

组织函数作为模板,能够天生实例对象。然则,偶然只能拿到实例对象,而该对象基础就不是由组织函数天生的,这时候能够运用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 张三.
  • 四、javascript运用prototype

JavaScript 的每一个对象都继续另一个对象,后者称为“原型”(prototype)对象。null也能够充任原型,区分在于它没有自身的原型对象。JavaScript 继续机制的设想就是,原型的一切属性和要领,都能被子对象同享。

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

Animal.prototype.color = 'white';

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

cat1.color // 'white'
cat2.color // 'white'

这里的color属性被两个猫同享了。

cat1.color = 'black';

cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';

在这里cat1.color 被赋值为 black ,所以cat1将不再运用原本的原型属性.

prototype对象有一个constructor属性,默许指向prototype对象地点的组织函数。由于constructor属性定义在prototype对象上面,意味着能够被一切实例对象继续。

<font color=blue>组织函数就是一般的函数, 所以实际上一切函数都有prototype属性。</font>

  • 五、_proto_原型链

对象的属性和要领,有能够定义在自身,也有能够定义在它的原型对象。由于原型自身也是对象,又有自身的原型,所以构成了一条原型链(prototype chain)。比方,a对象是b对象的原型,b对象是c对象的原型,以此类推。

假如一层层地上溯,一切对象的原型终究都能够上溯到Object.prototype,即Object组织函数的prototype属性。那末,Object.prototype对象有无它的原型呢?回复是有的,就是没有任何属性和要领的null对象,而null对象没有自身的原型。

“原型链”的作用是,读取对象的某个属性时,JavaScript 引擎先寻觅对象自身的属性,假如找不到,就到它的原型去找,假如照样找不到,就到原型的原型去找。假如直到最顶层的Object.prototype照样找不到,则返回undefined。

假如对象自身和它的原型,都定义了一个同名属性,那末优先读取对象自身的属性,这叫做“掩盖”(overriding)。

  • Object.prototype.__proto__

依据言语规范,__proto__属性只要浏览器才须要布置,其他环境能够没有这个属性,而且前后的两根下划线,示意它实质是一个内部属性,不应当对运用者暴露。因而,应当只管罕用这个属性,而是用Object.getPrototypeof()(读取)和Object.setPrototypeOf()(设置),举行原型对象的读写操纵。

this及this的绑定

this的动态切换,当然为JavaScript制造了庞大的灵活性,但也使得编程变得难题和隐约。偶然,须要把this牢固下来,防止涌现意想不到的状况。JavaScript供应了call、apply、bind这三个要领,来切换/牢固this的指向。

this的运用

这个关键字很好的表现了,假如不是面向对象,那为何运用this呢,实在这个this还真有点不一样,在java中this就是指的本类,那末js中呢?

  • this永久指向的是末了挪用它的对象,也就是看它实行的时刻是谁挪用的。

所以这里须要注重的题目就是,究竟是哪一个对象末了挪用了this,只要由于this自身指代的就是对象。下面的例子中this的指向变了,变成了谁(谁挪用了), 一览无余。

function f() {
  return '姓名:'+ this.name;
}

var A = {
  name: '张三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};

A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
  • 假如没有对象挪用他,默许是用window对象挪用的,这也讲得通,由于window是js的顶层对象,一切其他对象都是window的部属对象,JavaScript划定,浏览器环境的一切全局变量,都是window对象的属性。假如一个函数在全局环境中运转,那末this就是指顶层对象。

详细的例子看下面链接,比我写的好,
这里有细致的解说http://www.cnblogs.com/pssp/p/5216085.html

call的运用

函数实例的call要领,能够指定函数内部this的指向(即函数实行时地点的作用域),然后在所指定的作用域中,挪用该函数。call要领的参数,应当是一个对象。假如参数为空、null和undefined,则默许传入全局对象。

var obj = {};

var f = function () {
  return this;
};

f() === this // true
f.call(obj) === obj // true

上面代码中,在全局环境运转函数f时,this指向全局环境;call要领能够转变this的指向,指定this指向对象obj,然后在对象obj的作用域中运转函数f。

var n = 123;
var obj = { n: 456 };

function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

上面代码中,a函数中的this关键字,假如指向全局对象,返回效果为123。假如运用call要领将this关键字指向obj对象,返回效果为456。能够看到,假如call要领没有参数,或许参数为null或undefined,则等同于指向全局对象。

function add(a, b) {
  return a + b;
}

add.call(this, 1, 2) // 3

上面代码中,call要领指定函数add内部的this绑定当前环境(对象),而且参数为1和2,因而函数add运转后获得3。

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

// 掩盖掉继续的 hasOwnProperty 要领
obj.hasOwnProperty = function () {
  return true;
};
obj.hasOwnProperty('toString') // true

Object.prototype.hasOwnProperty.call(obj, 'toString') // false

上面代码中,hasOwnProperty是obj对象继续的要领,假如这个要领一旦被掩盖,就不会获得准确效果。call要领能够处理这个题目,它将hasOwnProperty要领的原始定义放到obj对象上实行,如许不管obj上有无同名要领,都不会影响效果。

apply的运用

apply要领的作用与call要领相似,也是转变this指向,然后再挪用该函数。唯一的区分就是,它吸收一个数组作为函数实行时的参数,运用花样以下。

function f(x,y){
  console.log(x+y);
}

f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2

上面的 f 函数原本接收两个参数,运用apply要领今后,就变成能够接收一个数组作为参数。

  • 绑定回调函数的对象

    var o = new Object();
    o.f = function () {
    console.log(this === o);
    }
    var f = function (){
    o.f.apply(o);
    // 或许 o.f.call(o);
    };
    $(‘#button’).on(‘click’, f);

点击按钮今后,控制台将会显现true。由于apply要领(或许call要领)不仅绑定函数实行时地点的对象,还会马上实行函数,因而不得不把绑定语句写在一个函数体内。更简约的写法是采纳下面引见的bind要领。

bind的运用

bind要领用于将函数体内的this绑定到某个对象,然后返回一个新函数。

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

var func = counter.inc;
func();
counter.count // 0
count // NaN

上面代码中,函数func是在全局环境中运转的,这时候inc内部的this指向顶层对象window,所以counter.count是不会变的,反而建立了一个全局变量count。由于window.count原本即是undefined,举行递增运算后undefined++就即是NaN。

为了处理这个题目,能够运用bind要领,将inc内部的this绑定到counter对象。

var func = counter.inc.bind(counter);
func();
counter.count // 1
    原文作者:Andy
    原文地址: https://segmentfault.com/a/1190000013215469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞