JavaScript是基于原型的语言,经过new实例化出来的对象,其属性和行为来自于两部分,一部分来自于构造函数,另外一部分是来自于原型。构造函数中定义的属性和行为的优先级比原型中定义的属性和优先级高,若是构造函数和原型定义了同名的属性和行为,构造函数中的属性和行为会覆盖原型中的同名的属性和行为。以下图——编程
当咱们声明一个类时,其实同时生成了一个对应的原型,例如咱们定义Animal这个类时,会生成一个与Animal类对应的原型,经过Animal.prototype能够指向这个原型,原型能够经过constructor指向Animal类,更确切的说,是指向Animal类的构造函数。app
function Animal() {} var a = Animal.prototype; b = a.constructor; alert(b === Animal); //true
//**************************** 1.开始应用原型 **************************** (function() { function Animal() {} // 旧的方式 // function Animal(name) { // this.name = name; // this.type = 'animal'; // this.say = function () { // alert('Hello'); // } // } Animal.prototype = { name : 'king', say : function() { //this.name和this.type均能访问到,与它们属性所在的前后顺序是无关的 console.log('I am a ' + this.type + ', my name is ' + this.name); }, type : 'animal' } //-----test----- var dog = new Animal(); dog.say(); //I am a animal, my name is king })();
//**************************** 2.属性与方法的分离 **************************** (function() { //咱们习惯把属性放在构造函数里面 function Animal(name) { this.name = name || 'king'; this.type = 'animal'; } //将方法放在类的原型里 Animal.prototype = { say : function() { console.log('I am a ' + this.type + ', my name is ' + this.name); } } //-----test----- var dog = new Animal(); dog.say(); //I am a animal, my name is king })();
//**************************** 3.公有和私有 **************************** //用this.xxx定义的属性是公有的,用var xxx定义的属性是私有的 (function() { function Animal(name) { this.name = name || 'king'; this.type = 'animal'; //私有变量 var age = 20; //私有方法 var move = function() { alert('I am a moving dog'); } } Animal.prototype = { say : function() { console.log('I am a ' + this.type + ', my name is ' + this.name); } } //-----test----- var dog = new Animal('wangcai'); console.log(dog.age); //undefined dog.say(); //I am a animal, my name is wangcai dog.move(); //报错,has no method 'move' })();
//**************************** 4.如何访问到私有变量(方法) **************************** (function() { function Animal(name) { this.name = name || 'king'; this.type = 'animal'; //私有变量 var age = 20; //私有方法 var move = function() { alert('I am a moving dog'); } //在构造器做用域(私有变量的做用域)中,构造一个可以访问私有变量的公有方法 this.say = function() { console.log('I am a ' + this.type + ', my name is ' + this.name + ', my age is ' + age); } this.act = function() { move(); } } Animal.prototype = {}; //-----test----- var dog = new Animal('wangcai'); dog.say(); //I am a animal, my name is wangcai, my age is 20 dog.act(); //I am a moving dog /** * 将全部属性和行为,不管公有仍是私有的属性和行为所有写在构造函数里,的确是最方便的方式,但并不推荐这么作。 * 由于在内存中一个类的原型只有一个,写在原型中的行为,能够被全部实例所共享,实例化的时候, * 并不会在实例的内存中再复制一份,而写在类里的行为,实例化的时候会在每一个实例里复制一份。 * 把行为写在原型里能够减小内存消耗,没有特殊缘由,推荐尽可能把行为写在原型里。 */ })();
//**************************** 5.如何访问到私有变量(方法) —— 性能改造**************************** /** * 写在原型里的行为必定是公有的,并且没法访问私有属性,因此如何处理私有行为和私有属性是个难题。 * 1>若是对属性和行为的私有性有很是高的强制性,咱们不得不牺牲内存,将私有行为放在构造函数里,实现真正的私有。 * 2>若是对属性和行为的私有性要求不高,更常见的作法是约定私有行为, * 经过给属性和行为的名称前面加上“_”来约定它是私有的,这是一种命名约定。 */ (function() { function Animal(name) { this.name = name || 'king'; this.type = 'animal'; //私有变量 this._age = 20; } Animal.prototype = { _move : function() { alert('I am a moving dog'); }, say : function() { //访问this._age来标识age属性是私有的 console.log('I am a ' + this.type + ', my name is ' + this.name + ', my age is ' + this._age); }, act : function() { //访问this._age来标识move方法是私有的 this._move(); } }; //-----test----- var dog = new Animal('wangcai'); dog.say(); //I am a animal, my name is wangcai, my age is 20 dog.act(); //I am a moving dog console.log(dog._age); //不推荐实例直接调用_age,违反命名约定 dog._move(); //I am a moving dog 不推荐实例直接调用_move,违反命名约定 })();
//**************************** 6.继承:继承构造函数 **************************** (function () { function Animal(name) { //应用函数调用模式,此时的this指向window this.name = name || 'king'; this.type = 'animal'; } Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } function Bird(name) { //此时的this是Bird Animal(name); //这个方法调用事后this就变成了window(函数调用模式) } //-----test----- var bird = new Bird('xiaocui'); console.log(bird.type); //undefined,此时的bird对象没有任何自身属性 })();
//**************************** 7.继承:继承构造函数 —— 改造 **************************** (function () { function Animal(name) { //此时的this指向Bird this.name = name || 'king'; this.type = 'animal'; } Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } //这里咱们只对构造器进行了继承操做,但对于Animal类里面的方法却没法继承过来 function Bird(name) { //此时的this是Bird Animal.call(this, name); //利用call方法改变做用域 } //-----test----- var bird = new Bird('xiaocui'); console.log(bird.type); //animal bird.say(); //报错,has no method 'say' })();
//**************************** 8.继承:继承原型 —— 1 **************************** //方式1:Bird.prototype = Animal.prototype; (function () { function Animal(name) { //初始化完这个函数后Animal.prototype.contructor === Animal; //true //此时的this指向Bird this.name = name || 'king'; this.type = 'animal'; } //对Animal.prototype从新赋值,那么此时的Animal.prototype.contructor === Animal; //false Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } function Bird(name) { Animal.call(this, name); } //原型继承 Bird.prototype = Animal.prototype; //-----test----- var bird = new Bird('xiaocui'); console.log(bird.type); //animal bird.say(); //I am a animal, my name is xiaocui })();
//**************************** 9.继承:继承原型 —— 2 **************************** //方式1:Bird.prototype = Animal.prototype; //给子类增长独有方法,如给Bird类增长fly方法 (function () { /** * Animal.prototype = { //Object * constructor: funciton Animal(name) {...}, * __proto__: Object * } */ function Animal(name) { this.name = name || 'king'; this.type = 'animal'; } /** * Animal.prototype = { //Object * say: funciton () {...}, * __proto__: Object * } */ Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } /** * Bird.prototype = { //Object * constructor: funciton Bird(name) {...}, * __proto__: Object * } */ function Bird(name) { Animal.call(this, name); } /** * 执行完这一句,Bird.prototype与Animal.prototype指向同一个对象,即 * Bird.prototype = Animal.prototype = { //Object * say: funciton () {...}, * __proto__: Object * } */ Bird.prototype = Animal.prototype; /** * 执行完这一句,增长Bird.prototype的属性(fly方法),即 * Bird.prototype = Animal.prototype = { * fly: function () {...}, * say: function () {...}, * __proto__: Object * } */ Bird.prototype.fly = function () { console.log('I am flying'); } //-----test----- var bird = new Bird('xiaocui'); bird.fly(); //I am flying var dog = new Animal('king'); dog.fly(); //I am flying /** * 因为Bird.prototype和Animal.prototype指向同一个地址,对Bird.prototype新增的方法, * 经过对Animal.prototype的属性访问也可以访问到Bird.prototype新增的方法。 * 显然,这并非咱们想要的。 */ })();
//**************************** 10.继承:继承原型 —— 3 **************************** //方式二:Bird.prototype = new Animal(); //给子类增长独有方法,如给Bird类增长fly方法 (function () { function Animal(name) { this.name = name || 'king'; this.type = 'animal'; } Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } function Bird(name) { Animal.call(this, name); } /** * 执行完这一句,把Animal原型中的say方法继承过来,即 * Bird.prototype = { * name: 'king', * type: 'animal', * __proto__: { * say: function () {...} * } * } */ Bird.prototype = new Animal(); /** * 执行完这一句,从新赋值Bird.prototype的构造器属性,即 * Bird.prototype = { * constructor: function Bird(name) {...}, * name: 'king', * type: 'animal', * __proto__: { * say: function () {...} * } * } */ Bird.prototype.constructor = Bird; /** * 执行完这一句,增长Bird.prototype的属性(fly方法),即 * Bird.prototype = { * constructor: function Bird(name) {...}, * fly: function () {...}, * name: 'king', * type: 'animal', * __proto__: { * say: function () {...} * } * } */ Bird.prototype.fly = function () { console.log('I am flying'); } //-----test----- var bird = new Bird('xiaocui'); bird.say(); //I am a animal, my name is xiaocui bird.fly(); //I am flying var dog = new Animal('king'); dog.fly(); //报错,has no method 'fly' })();
//**************************** 11.继承:封装继承函数 **************************** (function () { function extend(subClass, superClass) { var F = function () {}; //构建中间量 F.prototype = superClass.prototype; //中间量原型指向父类原型 subClass.prototype = new F(); //将父类原型赋值给子类原型,可以继承全部父类原型的方法 subClass.prototype.constructor = subClass; //子类原型构造器属性指回子类构造器,使得子类的方法不会新增或覆盖父类的方法 subClass.superclass = superClass.prototype; //给子类添加superclass属性,并将其赋值父类原型 //使得在子类构造器中成功利用superclass属性来调用父类构造器 if (superClass.prototype.constructor === Object.prototype.constructor) { superClass.prototype.constructor = superClass; //从新赋值父类原型构造器为父类 } } function Animal(name) { this.name = name || 'king'; this.type = 'animal'; } Animal.prototype = { say: function () { console.log('I am a ' + this.type + ', my name is ' + this.name); } } function Bird(name) { //具有通用性 this.constructor.superclass.constructor.apply(this, arguments); } extend(Bird, Animal); Bird.prototype.fly = function () { alert('I am flying'); } //-----test----- var bird = new Bird('xiaocui'); bird.say(); //I am a animal, my name is xiaocui bird.fly(); //I am flying })();
//**************************** 12.面向过程与面向对象 **************************** /** * 面向过程编程 * 1.这种编程方式将程序分红了“数据”和“处理函数”两个部分,程序以“处理函数”为核心, * 若是要执行什么操做,就将“数据”传给相应的“处理函数”,返回咱们须要的结果。这种编程方式就是面向过程编程。 * 2.这种编程方式存在的问题以下: * 1> 数据和处理函数之间没有直接的关联,在执行操做的时候,咱们不但要选择相应的处理函数, * 还要本身准备处理函数须要的数据,也就是说,在执行操做的时候,咱们须要同时关注处理函数和数据。 * 2> 数据和处理函数都暴露在同一个做用域内,没有私有和公有的概念,整个程序中全部的数据和处理函数均可以相互访问, * 在开发阶段初期也许开发速度很快,但到了开发后期和维护阶段,因为整个程序耦合得很是紧, * 任何一个处理函数和数据都有可能关联到其余地方,容易牵一发而动全身,从而加大了修改难度。 * 3> 面向过程的思惟方式是典型的计算机思惟方式——输入数据给处理器,处理器内部执行运算,处理器返回结果。 * 也就是说面向过程的思惟方式是在描述一个个“动做”。 * 而现实生活中的一个个“物件”(如人{姓名,状态})很难用面向过程的思惟方式进行描述。 * * 面向对象编程 * 这种编程就是抛开计算机思惟,使用生活中的思惟进行编程的编程方式。面向过程的思惟就是描述一个个“动做”, * 而面向对象的思惟就是描述一个个“物件”,客观生活中的物件,在程序中咱们管“物件”叫作“对象”, * 对象由两部分组成:“属性”和“行为”,对应客观世界中物件的“状态”和“动做”。 */ //面向过程 (function () { //定义电话本 var phonebook = [ {name: 'adang', tel: '1111'}, {name: 'king', tel: '2222'} ]; //查询电话 function getTel(oPhoneBook, oName) { var tel = ''; for (var i = 0; i < oPhoneBook.length; i++) { if (oPhoneBook[i].name === oName) { tel = oPhoneBook[i].tel; break; } } return tel; } //添加记录 function addItem(oPhonebook, oName, oTel) { oPhonebook.push({name: oName, tel: oTel}); } //删除记录 function removeItem(oPhonebook, oName) { var index; for (var i = 0; i < oPhonebook.length; i++) { if (oPhonebook[i].name === oName) { index = i; break; } } if (index !== undefined) { oPhonebook.splice(index, 1); } } //-----test----- //调用函数-增 addItem(phonebook, 'xiaoxiao', '3333'); //调用函数-删 removeItem(phonebook, 'xiaoxiao'); //调用函数-查 getTel(phonebook, 'king'); })(); //面向对象 (function () { //构造函数专一的是对象所具备的属性 function PhonebookManager(oPhonebook) { this._phonebook = oPhonebook; } //全部方法均在构造函数的原型中添加 PhonebookManager.prototype = { addItem: function (oName, oTel) { this._phonebook.push({name: oName, tel: oTel}); }, removeItem: function (oName) { var index, phonebook = this._phonebook; for (var i = 0, len = phonebook.length; i < len; i++) { if (phonebook[i].name === oName) { index = i; break; } } if (index !== undefined) { phonebook.splice(index, 1); } }, getTel: function (oName) { var tel = '', phonebook = this._phonebook; for (var i = 0; i < this._phonebook.length; i++) { if (phonebook.name === oName) { tel = phonebook.tel; break; } } return tel; } } //------test----- var myPhoneManager = new PhonebookManager([ {name: 'adang', tel: '1111'}, {name: 'king', tel: '2222'} ]); //调用函数-增 myPhoneManager.addItem('xiaoxiao', '3333'); //调用函数-删 myPhoneManager.removeItem('xiaoxiao'); //调用函数-查 myPhoneManager.getTel('king'); })();