1. 原始类型和对象类型
var n = 10; var m = n; // 传值赋值,m的值等于n m = 13; console.log('n = %d, m = %d.', n, m); // n = 10, m = 13. var onePerson = {}; // 新建一个空对象 onePerson.fullname = 'HarryPotter'; // 为对象新建属性fullname onePerson.age = 21; // 为对象新建属性age onePerson.talk = function () { console.log('Hi, my name is %s. My age is %d.', this.fullname, this.age); } // 为对象新建方法talk,在对象的方法中,this指向该对象 var otherPerson = onePerson; // 对象赋值是引用赋值,otherPerson就是onePerson,二者共同指向一块内存地址。 otherPerson.fullname = 'Kate'; // 等同于修改了onePerson.fullname onePerson.talk(); // Hi, my name is Kate. My age is 21.
2. 将函数作为构造函数
javascript的函数主要有三种调用方式。作为函数直接调用,作为对象的方法调用,作为对象的构造函数调用。
function Person() { global_firstname = 'funway'; // 定义一个全局变量,无法被对象继承 var local_lastname = 'wang'; // 定义一个局部变量,无法被对象继承 this.fullname = 'funway.wang'; // 定义一个继承属性,可以被对象继承 console.log('In Person function as a constructor.'); } Person(); // 直接作为函数调用,打印 "In Person function as a constructor." AA = new Person(); // 作为构造函数调用, 同样会打印 "In Person function as a constructor." console.log(global_firstname); // "funway" console.log(AA.global_firstname); // undefined console.log(AA.local_lastname); // undefined console.log(AA.fullname); // "funway.wang"
上面的代码中,我们通过将Person函数作为构造函数创建了一个新的对象AA,用C++的面向对象来看,函数名Person就相当于一个类名。
实际上,Person作为一个javascript函数,默认有一个prototype属性。prototype属性值是一个对象,我们叫做原型对象。当函数作为构造函数被调用时候,该prototype原型对象就会被当作新对象的原型,所有新创建的对象均有一个__proto__对象,指向该原型对象。另外,prototype原型对象内部有一个constructor属性,指向构造函数Person,这个constructor负责调用构造函数对新对象进行初始化。可以这么说,AA = new Person() 相当于执行了以下三步:
AA = {}; // 创建空对象 AA.__ptoto__ = Person.prototype; 执行AA.__proto__.constructor指向的函数对AA进行初始化,创建AA对象的属性
上图中的Object与Function这俩个底层的概念这里就不多解释了,javascript的Object与Function的关系看到哭。。。=。=#
3. prototype原型,原型链与动态继承
从上面可以看出,由Person构造函数构造的对象,都有一个默认的__proto__属性指向Person.prototype,那么我对Person.prototype的修改都会实时的传递到所有Person对象上。另外,对象可以直接通过.属性名/.方法名访问到其原型对象的属性或者方法,前提是该对象本地没有重名的属性/方法。这就是javascript动态继承的基础。而原型链的意义就是,要访问一个对象x的属性/方法的时候,会先查找对象本地空间,没有,则查找x.__proto__空间,然后再x.__proto__.__proto__。这样递归直到找到所要的属性/方法或到达底层的Object基本对象。
function Person(name) { this.name = name? name : 'noname'; } bill = new Person('bill'); // 创建新对象 kate = new Person('kate'); // 创建新对象 console.log(bill.name); // "bill" console.log(bill.age); // undefined,bill对象没有该属性 Person.prototype.age = 18; // 增加age属性,所有的Person对象都自动拥有该属性。 Person.prototype.sayHi = function() { console.log('hi, my name is %s, i am %d yeas old.', this.name, this.age); } // 增加sayHi方法,所有的Person对象都自动拥有该方法 bill.sayHi(); // "hi, my name is bill, i am 18 yeas old." kate.sayHi(); // "hi, my name is kate, i am 18 yeas old." bill.age = 25; // 给bill对象添加了一个本地属性age,之后bill.age将不再访问bill.__proto__.age console.log(bill.age); // 25,访问的是bill.age(不会再跳进去访问bill.__proto__.age了) console.log(kate.age); // 18,访问的是kate.__proto__.age(因为kate.age不存在) var Programer = function(name) { this.name = name? name : 'noname'; } Programer.prototype.__proto__ = Person.prototype; Programer.prototype.skill = function() { console.log('coding'); } var jim = new Programer('jim'); jim.sayHi(); // 调用的是jim.__proto__.__proto__.sayHi() jim.skill(); // 调用的是jim.__proto__.skill