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