封装
封装数据
在很多言语的对象体系中,封装数据是由语法剖析来完成的,这些言语或许供应了 private、
public、protected 等症结字来供应差别的接见权限。比方:java
但在js内里,并没有供应这些症结字的支撑,我们只能经由历程作用域来模仿完成封装性。(ES6 let除外)
var myTest = (function() {
var _name ='jason';
return {
getName: function(){
return _name;
}
}
})()
console.log(myTest.getName()); // 输出:jason
console.log(_name); // 输出:_name is not defined
这里,我们定义了一个自实行函数(什么叫自实行函数?参考这篇文章),函数内部定义了一个变量_name
,函数return一个对象出去(也叫模块形式),向外部暴露了一个getName
函数,这里的_name
变量就被封装在了myTest
函数内部。
上文说的是封装数据,在js里,不光数据能够封装,还能够封装完成、封装范例,封装变化等,封装使得对象之间的耦合变得松懈,对象之间只经由历程暴露的 API 接口来通讯。
原型形式和基于原型继续的JavaScript对象体系
在java中,对象必须由类建立而来,而在js中,对象倒是经由历程原型继续的体式格局得来的,在原型编程的头脑中,类并非必须的,对象未必须要从类中建立而来, 一个对象是经由历程克隆别的一个对象所获得的。
原型形式不单是一种设想形式,也被称为一种编程泛型。
运用克隆的原型形式
从设想形式的角度讲,原型形式是用于建立对象的一种形式,假如我们想要建立一个对象, 一种要领是先指定它的范例,然后经由历程类来建立这个对象。原型形式挑选了别的一种体式格局,我们 不再体贴对象的细致范例,而是找到一个对象,然后经由历程克隆来建立一个如出一辙的对象。
原型形式的完成症结,是言语本身是不是供应了 clone 要领。ECMAScript 5 供应了 Object.create 要领,能够用来克隆对象。代码以下:
var Plane = function(){
this.blood = 100;
this.attackLevel = 1;
this.defenseLevel = 1;
};
var plane = new Plane();
plane.blood = 500;
plane.attackLevel = 10;
plane.defenseLevel = 7;
var clonePlane = Object.create( plane );
console.log( clonePlane ); // 输出:Object {blood: 500, attackLevel: 10,defenseLevel: 7}
在不支撑 Object.create 要领的浏览器中,则能够运用以下代码:
Object.create = Object.create || function( obj ){
var F = function(){};
F.prototype = obj;
return new F();
}
原型编程范型的一些划定规矩
- 一切的数据都是对象。
- 要获得一个对象,不是经由历程实例化类,而是找到一个对象作为原型并克隆它。
- 对象会记着它的原型。
- 假如对象没法相应某个要求,它会把这个要求托付给它本身的原型。
JavaScript中的原型继续
这里我们来依据上面的范式来整顿一下js中遵照的划定规矩
一切的数据都是对象
在js中,除了undefined以外,一切数据都是对象,number、boolean、string 这几种基础范例数据也能够经由历程“包装类”的体式格局变成对象范例数据来处置惩罚。那末依据原型划定规矩,这些对象一定有个根对象,这个对象就是Object.prototype
,Object.prototype 对象是一个空的 对象。我们在 JavaScript 碰到的每一个对象,实际上都是从 Object.prototype 对象克隆而来的, Object.prototype 对象就是它们的原型。
var obj1 = new Object();
var obj2 = {};
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true
要获得一个对象,不是经由历程实例化类,而是找到一个对象作为原型并克隆它
js中克隆是引擎内部完成的,我们不用去体贴他是怎样完成的,我们只需晓得运用var obj1 = new Object()
或许var obj2 = {}
,引擎就会从Object.prototype
克隆一个对象出来。
接下来我们看看怎样运用new
运算符获得一个对象
var Person = function(name) {
this.name = name;
this.getName = function() {
return this.name;
}
}
var p = new Person('jason');
console.log(p.name);
console.log(p.getName());
console.log(Object.getPrototypeOf(p) === Person.prototype); // 输出true
在 JavaScript 中没有类的观点,这句话我们已反复过很屡次了。但适才不是明显挪用了new
Person()吗?
*在这里 Person 并非类,而是函数组织器,JavaScript 的函数既能够作为一般函数被挪用, 7 也能够作为组织器被挪用。当运用 new 运算符来挪用函数时,此时的函数就是一个组织器。 用
new 运算符来建立对象的历程,实际上也只是先克隆 Object.prototype 对象,再举行一些其他额 外操纵的历程。*
对象会记着它的原型
JavaScript 给对象供应了一个名为__proto__的隐蔽属性,某个对象的__proto__属性默许会指 向它的组织器的原型对象,即{Constructor}.prototype。
我们经由历程代码来考证:
var objA = {
name: 'jason'
}
console.log(objA.__proto__ === Object.prototype); //true
再来
var objB = function(age) {
this.age = age;
}
var b = new objB();
console.log(b.__proto__ === objB.prototype); //true
<span style=”color:red”>实际上,__proto__就是对象跟“对象组织器的原型”联系起来的纽带</span> 牢记这句话,对将来明白js原型链很有协助。
假如对象没法相应某个要求,它会把这个要求托付给它的组织器的原型
虽然 JavaScript 的对象最初都是由 Object.prototype 对象克隆而来的,但对象组织器的原型并不仅限于 Object.prototype 上,能够动态指向其他对象。
var obj = { name: 'sven' };
var A = function(){};
A.prototype = obj;
var a = new A();
console.log(a.__proto__ === obj); //true
console.log(a.name); // 输出:sven
上面的代码中,第一行和第二行本没有任何关联,obj
是个对象字面量建立的对象,A
是个空要领,在第三行代码实行之前,obj
的__proto__
指向Object.prototype
,A
的prototype
指向本身的组织器,A.prototype = obj;
将援用指向了obj
,所以在代码实行完后,对象a的原型指向obj,虽然a本身没有name属性,但原型上具有name属性
总结
如今,让我们来总结一下js建立对象的几种体式格局:
- 对象字面量
var obj = {};
- 经由历程对象组织器建立
var Co = function(){};
var obj = new Co();
- 经由历程Object.create();建立(ES5今后版本支撑)
var Co = function(){};
var obj = Object.create(Co);
- 经由历程class建立(ES6今后版本支撑)
class An{
constructor(name){
this.name = name;
}
getName() {
return this.name;
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
speak() {
return "woof";
}
}
var dog = new Dog("Scamp");
console.log(dog.getName()); // Scamp
console.log(dog.speak()); // woof
上面的几种建立对象的体式格局有实质的区分,这里先不做细致申明,后续学完作用域和闭包后再一致申明。