javascript学习五---OOP
面向对象:JavaScript的所有数据都可以看成对象 JavaScript的面向对象编程和大多数其他语言如Java、C#的面向对象编程都不太一样。如果你熟悉Java或C#,很好,你一定明白面向对象的两个基本概念: 面向对象的两个基本概念: 1、类:类是对象的类型模板,例如,定义Student类来表示学生,类本身是一种类型,Student表示学生类型,但不表示任何具体的某个学生; 2、实例:实例是根据类创建的对象,例如,根据Student类可以创建出xiaoming、xiaohong、xiaojun等多个实例,每个实例表示一个具体的学生,他们全都属于Student类型。 所以,类和实例是大多数面向对象编程语言的基本概念。 JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。 JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。 JS的prototype和__proto__ 一、prototype和__proto__的概念 prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。 __proto__是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性 用chrome和FF都可以访问到对象的__proto__属性,IE不可以。 二、new 的过程 var Person = function(){}; var p = new Person(); new的过程拆分成以下三步: (1) var p={}; 也就是说,初始化一个对象p (2) p.__proto__ = Person.prototype; (3) Person.call(p); 也就是说构造p,也可以称之为初始化p 关键在于第二步,我们来证明一下: var Person = function(){}; var p = new Person(); alert(p.__proto__ === Person.prototype); 三、示例 var Person = function(){}; Person.prototype.sayName = function() { alert("My Name is Jacky"); }; Person.prototype.age = 27; var p = new Person(); p.sayName(); p是一个引用指向Person的对象。我们在Person的原型上定义了一个sayName方法和age属性,当我们执行p.age时,会先在this的内部查找(也就是构造函数内部),如果没有找到然后再沿着原型链向上追溯。 这里的向上追溯是怎么向上的呢?这里就要使用__proto__属性来链接到原型(也就是Person.prototype)进行查找。最终在原型上找到了age属性 为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。 案例: function Cat(name,color){ this.name=name; this.color=color; } 我们现在就可以生成实例对象了。 var cat1 = new Cat("大毛","黄色"); var cat2 = new Cat("二毛","黑色"); alert(cat1.name); // 大毛 alert(cat1.color); // 黄色 这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。 alert(cat1.constructor == Cat); //true alert(cat2.constructor == Cat); //true Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。 alert(cat1 instanceof Cat); //true alert(cat2 instanceof Cat); //true 五、 Prototype模式 Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。 这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。 这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。 function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype.type = "猫科动物"; Cat.prototype.eat = function(){alert("吃老鼠")}; 六、 Prototype模式的验证方法 6.1 isPrototypeOf() 这个方法用来判断,某个proptotype对象和某个实例之间的关系。 alert(Cat.prototype.isPrototypeOf(cat1)); //true alert(Cat.prototype.isPrototypeOf(cat2)); //true 6.2 hasOwnProperty() 每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。 alert(cat1.hasOwnProperty("name")); // true alert(cat1.hasOwnProperty("type")); // false 6.3 in运算符 in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。 alert("name" in cat1); // true alert("type" in cat1); // true in运算符还可以用来遍历某个对象的所有属性。 for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); } 比如,现在有一个"动物"对象的构造函数。 function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数。 function Cat(name,color){ this.name = name; this.color = color; } 怎样才能使"猫"继承"动物"呢? 一、 构造函数绑定 第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行: function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 二、 prototype模式 第二种方法更常见,使用prototype属性。 如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。 Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 任何一个prototype对象都有一个constructor属性,指向它的构造函数。 案例: class Student { constructor(name) { this.name = name; } hello() { console.log('Hello, ' + this.name + '!'); } }; var xiaoming = new Student('小明'); xiaoming.hello(); 结果:Hello, 小明! class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 记得用super调用父类的构造方法! this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } }; var teddy = new PrimaryStudent('teddy','no.1'); teddy.myGrade(); 结果:'I am at grade no.1'
心有猛虎,细嗅蔷薇