推荐好文:悟透javascript www.cnblogs.com/zhangshiwen…javascript
编程世界里只存在两种基本元素,一个是数据,一个是代码。编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力。html
要理解JavaScript,你得首先放下对象和类的概念,回到数据和代码的本原。前面说过,编程世界只有数据和代码两种基本元素,而这两种元素又有着纠缠不清的关系。JavaScript就是把数据和代码都简化到最原始的程度。java
JavaScript中的数据很简洁的。简单数据只有 undefined, null, boolean, number和string这五种,而复杂数据只有一种,即object。这就比如中国古典的朴素惟物思想,把世界最基本的元素归为金木水火土,其余复杂 的物质都是由这五种基本元素组成。es6
JavaScript中的代码只体现为一种形式,就是function。编程
任何一个JavaScript的标识、常量、变量和参数都只是unfined, null, bool, number, string, object 和 function类型中的一种,也就typeof返回值代表的类型。除此以外没有其余类型了。数组
在咱们学习编程语言时,都会遇到‘类’这个概念。咱们知道学习就是掌握知识从无到有的过程,可是有没有想过为何会有‘类’,必定要有它吗?浏览器
var life = {};
for (life.age = 1; life.age <= 3; life.age++)
{
switch(life.age)
{
case 1: life.body = "卵细胞";
life.say = function(){alert(this.age+this.body)};
break;
case 2: life.tail = "尾巴";
life.gill = "腮";
life.body = "蝌蚪";
life.say = function(){alert(this.age+this.body+"-"+this.tail+","+this.gill)};
break;
case 3: delete life.tail;
delete life.gill;
life.legs = "四条腿";
life.lung = "肺";
life.body = "青蛙";
life.say = function(){alert(this.age+this.body+"-"+this.legs+","+this.lung)};
break;
};
life.say();
};
复制代码
看这段代码一开始产生了一个生命对象life,通过后面的进化后变成了青蛙,固然继续进化可能变成其它你想不到的东西。bash
在现实世界里一开始也是没有”类“的,都是由分子原子组成,但为何会出来哺乳类,飞禽类嘞?这就是人的聪明之处了,人为归类,为了方便咱们后续处理事情。app
计算机是人创造出来的,编程也是人的逻辑思想,对某一类事物进行归类,能为咱们后续处理事物解决很多麻烦。框架
因此js中没有类这种概念上的强制,也能解决问题。
也能够有类(es6中已经推出了class概念),用了class后人在编程时能够更好的抽象编程里的世界了。
任何一个函数均可觉得其动态地添加或去除属性,这些属性能够是简单类型,能够是对象,也能够是其余函数。也就是说,函数具备对象的所有特征,你彻底能够把函数当对象来用。
其实,函数就是对象,只不过比通常的对象多了一个括号“()”操做符,这个操做符用来执行函数的逻辑。即,函数自己还能够被调用,通常对 象却不能够被调用,除此以外彻底相同。
自从有了对象,编程世界就被划分红两部分,一个是对象内的世界,一个是对象外的世界。 对象天生具备自私的一面,外面的世界未经容许是不可访问对象内部的。对象也有大方的一面,它对外提供属性和方法,也为他人服务。
既然是对象这种具体的事物了,有没有想过”对象的意识“。
就在对象将世界划分为内外两部分的同时,对象的“自我”也就随之产生。“自我意识”是生命的最基本特征!正是因为对象这种强大的生命力,才使得编程世界充满无限的生机和活力。this就是对象的意识
通常编程语言的this就是对象本身,而JavaScript的this却并不必定!this多是我,也多是你,多是他,反正是我中有你,你中有我。因此this就像意识同样,不是一层不变的,不一样的时间,地点,场景你可能会有不一样的意识。看,js多美。
function WhoAmI() //定义一个函数WhoAmI
{
alert("I'm " + this.name + " of " + typeof(this));
};
WhoAmI(); //此时是this当前这段代码的全局对象,在浏览器中就是window对象,其name属性为空字符串。输出:I'm of object var BillGates = {name: "Bill Gates"}; BillGates.WhoAmI = WhoAmI; //将函数WhoAmI做为BillGates的方法。 BillGates.WhoAmI(); //此时的this是BillGates。输出:I'm Bill Gates of object
var SteveJobs = {name: "Steve Jobs"};
SteveJobs.WhoAmI = WhoAmI; //将函数WhoAmI做为SteveJobs的方法。
SteveJobs.WhoAmI(); //此时的this是SteveJobs。输出:I'm Steve Jobs of object WhoAmI.call(BillGates); //直接将BillGates做为this,调用WhoAmI。输出:I'm Bill Gates of object
WhoAmI.call(SteveJobs); //直接将SteveJobs做为this,调用WhoAmI。输出:I'm Steve Jobs of object BillGates.WhoAmI.call(SteveJobs); //将SteveJobs做为this,却调用BillGates的WhoAmI方法。输出:I'm Steve Jobs of object
SteveJobs.WhoAmI.call(BillGates); //将BillGates做为this,却调用SteveJobs的WhoAmI方法。输出:I'm Bill Gates of object WhoAmI.WhoAmI = WhoAmI; //将WhoAmI函数设置为自身的方法。 WhoAmI.name = "WhoAmI"; WhoAmI.WhoAmI(); //此时的this是WhoAmI函数本身。输出:I'm WhoAmI of function
({name: "nobody", WhoAmI: WhoAmI}).WhoAmI(); //临时建立一个匿名对象并设置属性后调用WhoAmI方法。输出:I'm nobody of object 复制代码
从上面的代码能够看出,同一个函数能够从不一样的角度来调用,this并不必定是函数自己所属的对象。this只是在任意对象和function元素结合时的一个概念。
看科幻电影常常有将本身的意识转移到其它事物上的身影。看,js就有这种能力。
// 建立一个没有任何属性的对象:
var o = {};
// 建立一个对象并设置属性及初始值:
var person = {
name: "Angel",
age: 18,
married: false
};
// 建立一个对象并设置属性和方法:
var speaker = {
text: "Hello World",
say: function(){alert(this.text)}
};
// 建立一个更复杂的对象,嵌套其余对象和对象数组等:
var company = {
name: "Microsoft",
product: "softwares",
chairman: {
name: "Bill Gates",
age: 53,
Married: true
},
employees: [
{name: "Angel", age: 26, Married: false},
{name: "Hanson", age: 32, Marred: true}
],
readme: function() {document.write(this.name + " product " + this.product);}
};
复制代码
function MyFunc() {}; //定义一个空函数
var anObj = new MyFunc(); //使用new操做符,借助MyFun函数,就建立了一个对象
复制代码
这种写法如何理解
// 等价于这种形式(简陋版)
function MyFunc(){};
var anObj = {}; //建立一个对象
MyFunc.call(anObj); //将anObj对象做为this指针调用MyFunc函数
复制代码
执行new命令会通过如下几个步骤
function _new (person, ...rest) {
// 建立一个空对象,这个对象将会是返回的对象实例
var obj = {};
// 将这个空对象的原型指向person的prototype属性;
obj.__prototype__ = person.prototype;
// 上述两步能够合为一步 : var obj = Object.create(person.prototype)
//将person的this指向空对象,并运行person函数,apply命令绑定this后就会运行person
var res = person.apply(obj, rest);
// 判断res返回的是否是对象,是的话返res,不是的话返回以前建立的obj,没有返回值默认返回obj
return (typeof res === 'object' && res != null) ? res: obj;
}
复制代码
看前面知道咱们能够根据函数new出不少对象。那为何咱们不能写一些生成实例对象的函数,做为对象的模板,描述实例对象的基本结构呢?很显然js就是这样的。
咱们称能够生成多个实例对象,描述实例对象的基本结构的函数为构造函数。若是你愿意把构造函数看成“类”的话,她就是“类”,由于她原本就有“类”的那些特征。
function SayHello() { // 先定义一份SayHello函数代码
alert("Hello, I'm " + this.name);
};
function Person(name) { // 带参数的构造函数
this.name = name; // 将参数值赋给给this对象的属性
this.SayHello = SayHello; //给this对象SayHello方法赋值为前面那份SayHello代码。
};
var BillGates = new Person("Bill Gates"); //建立BillGates对象
var SteveJobs = new Person("Steve Jobs"); //建立SteveJobs对象
alert(BillGates.SayHello == SteveJobs.SayHello); //显示:true
复制代码
看这段代码虽然达到了共享了一份方法代码的目的,可是反映不出SayHello方法与Person类的关系,若是将SayHello方法写到Person构造函数里,那每次new出来的对象里都有各自的SayHello方法,在内存上是种浪费。
Javascript创造者在创造时考虑到了这种状况。因此JavaScript的全部function类型的对象都有一个prototype属性。这个prototype属性自己又是一个object类型的对 象,所以咱们也能够给这个prototype对象添加任意的属性和方法。
咱们将prototype属性称之为原型
function Person(name)
{
this.name = name; // 设置对象属性,每一个对象各自一份属性数据
};
Person.prototype.SayHello = function() // 给Person函数的prototype添加SayHello方法。
{
alert("Hello, I'm " + this.name);
}
var BillGates = new Person("Bill Gates"); // 建立BillGates对象
var SteveJobs = new Person("Steve Jobs"); // 建立SteveJobs对象
BillGates.SayHello(); // 经过BillGates对象直接调用到SayHello方法
SteveJobs.SayHello(); // 经过SteveJobs对象直接调用到SayHello方法
alert(BillGates.SayHello == SteveJobs.SayHello); // 由于两个对象是共享prototype的SayHello,因此显示:true
复制代码
看这段代码,咱们将SayHello方法写到Person的原型上,这样能体现出SayHello方法与Person类的关系。
function Person(name) {
this.name = name;
};
Person.prototype.company = "Microsoft"; // 原型的属性
Person.prototype.SayHello = function() { // 原型的方法
alert("Hello, I'm " + this.name + " of " + this.company);
};
var BillGates = new Person("Bill Gates");
BillGates.SayHello(); // 因为继承了原型的东西,规规矩矩输出:Hello, I'm Bill Gates. var SteveJobs = new Person("Steve Jobs"); SteveJobs.company = "Apple"; // 设置本身的company属性,掩盖了原型的company属性 SteveJobs.SayHello = function() { // 实现了本身的SayHello方法,掩盖了原型的SayHello方法 alert("Hi, " + this.name + " like " + this.company + ", ha ha ha "); }; SteveJobs.SayHello(); // 都是本身覆盖的属性和方法,输出:Hi, Steve Jobs like Apple, ha ha ha BillGates.SayHello(); // SteveJobs的覆盖没有影响原型对象,BillGates仍是按老样子输出 复制代码
这与面向对象里的“多态”很像,能够实现本身的方法。
看原型的多态咱们想到,能够随时给原型对象动态添加新的属性和方法,从而动态地扩展基类的功能特性,这是其它语言所不能具有的。
function Person(name) { // 基类构造函数
this.name = name;
};
Person.prototype.SayHello = function() { // 给基类构造函数的prototype添加方法
alert("Hello, I'm " + this.name);
};
function Employee(name, salary) { // 子类构造函数
Person.call(this, name); // 调用基类构造函数
this.salary = salary;
};
Employee.prototype = new Person(); // 建一个基类的对象做为子类原型的原型,这里就是原型链继承了
Employee.prototype.ShowMeTheMoney = function() { //给子类添构造函数的prototype添加方法
alert(this.name + " $" + this.salary);
};
var BillGates = new Person("Bill Gates"); // 建立基类Person的BillGates对象
var SteveJobs = new Employee("Steve Jobs", 1234); // 建立子类Employee的SteveJobs对象
BillGates.SayHello(); // 经过对象直接调用到prototype的方法
SteveJobs.SayHello(); // 经过子类对象直接调用基类prototype的方法,关注!
SteveJobs.ShowMeTheMoney(); // 经过子类对象直接调用子类prototype的方法
alert(BillGates.SayHello == SteveJobs.SayHello); //显示:true,代表prototype的方法是共享的
复制代码
看这段代码,咱们知道Employee子类中没有SayHello方法,而Person基类中有SayHello方法,由于Employee原型上建一个基类的对象做为子类原型的原型,因此用Employee类new出来的对象能够访问到父亲的父亲的SayHello方法。
这种对象的属性和方法追溯机制是经过所谓的prototype链(原型链)来实现的。
在原型链的最末端,就是Object构造函数prototype属性指向的那一个原型对象。这个原型对象是全部对象的最老祖先,这个老祖宗实现了诸如 toString等全部对象天生就该具备的方法。其余内置构造函数,如Function, Boolean, String, Date和RegExp等的prototype都是从这个老祖宗传承下来的,但他们各自又定义了自身的属性和方法,从而他们的子孙就表现出各自宗族的那些 特征。
咱们前面说的都是ES5中的构造函数,在ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,做为对象的模板,经过class关键字,能够定义类。
基本上,ES6 的class能够看做只是一个语法糖,它的绝大部分功能,ES5均可以作到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
更多详细介绍请看阮一峰的ECMAScript 6 入门 es6.ruanyifeng.com/#docs/class
谨以此文膜拜李战(leadzen)的悟透javascript www.cnblogs.com/zhangshiwen… 但愿咱们对javascript世界都全部了解