1、对象
ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象,而ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。那如何在JS中实现面对对象编程呢?实际上,JS语言是通过一种叫做 原型(prototype)的方式来实现面向对象编程的。
- 创建对象
在JS里面创建对象有3种方式:
1、子面量
语法:var 对象名={属性,方法};
实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>创建学生对象</title> </head> <body> <script> var student = { name:"张三", sex:"男", age:18, sayHi:function () { alert("大家好,我是"+this.name+",性别:"+this.sex+",今年"+this.age+"岁"); } } student.sayHi(); </script> </body> </html>
缺陷:它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的,(每个实例对象没有自己的特性)
解决方式:初始化的值都一直每个实例对象没有自己的特性,伪类可以解决问题,
使用组合(构造函数+原型模式)解决传参合共享的问题。
2、调用系统的构造函数创建对象
语法:var 对象名=new Object();
实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>创建学生对象</title> </head> <body> <script> var student = new Object(); student.name="张三"; //属性 student.sex="男"; student.age=18; student.sayHi=function () { alert("大家好,我是"+this.name+",性别:"+this.sex+",今年"+this.age+"岁"); } student.sayHi(); </script> </body> </html>
缺陷:上面创建了一个对象,并且创建属性和方法,在sayHi()方法里的 this,就是代表 student对象
本身。这种是 JS创建对象最基本的方法,但有个缺点,想创建一个类似的对象,就会产生大量的代码。
解决方式:使用工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。
解决例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>工厂方法</title> </head> <body> <script> //创建对象的方法 function createStudent(name,sex,age) { var student = new Object(); student.name =name; student.sex=sex; student.age =age; student.sayHi=function () { alert("大家好,我是"+this.name+",性别:"+this.sex+",今年"+this.age+"岁"); } return student; } var person1 =createStudent("张三","男","18"); person1.sayHi(); var person2 = createStudent("李四","女","20"); person2.sayHi(); </script> </body> </html>
3、自定义构造函数方式创建对象:
语法:function 对象名(属性....);
实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>构造函数</title> </head> <body> <script> /* 构造函数也是函数,函数名首字母大写,主要用于创建对象。 经历的步骤: 1.创建一个对象 2.将构造函数的作用域给到新的对象 3.执行构造函数代码 4.返回对象 * */ function Student(name,sex,age) { this.name=name; this.sex=sex; this.age=age; this.sayHi=function () { alert("大家好,我是"+this.name+",性别:"+this.sex+",今年"+this.age+"岁"); } } var student1 = new Student("张三","男","18"); student1.sayHi(); //constructor构造器 instanof alert(student1.constructor==Student); alert("instanceof Student:"+(student1 instanceof Student) );//测试是不是指向Student alert("instanceof Object:"+(student1 instanceof Object) );//测试是不是指向Object </script> </body> </html>
缺陷:每次创建一个Student, 都给我创建了一个sayHi()的实例。 同一个功能的方法,有着多个相同实例
解决方式:原型方法
2、原型
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法 。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中 。
实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型</title> </head> <body> <script> /* * 在javascript中,每一个函数都有一个prototype属性 ,prototype是一个指针,指向对象。 * */ function Student() {//创建构造函数 } Student.prototype.name ="张三";//在原型里添加属性 Student.prototype.sex="男"; Student.prototype.age="19"; Student.prototype.sayHi=function () {//在原型里添加方法 alert("大家好,我是"+this.name+",性别:"+this.sex+",今年"+this.age+"岁"); } var student1 = new Student();//实例化对象 student1.sayHi(); var student2 = new Student(); student2.sayHi(); </script> </body> </html>
2.1构造函数的和原型模式的关系如图:
2.2原型-继承
在JS中实现继承利用原型链的关系去实现继承的。每个函数都有一个原型对象,每一个实例对象都有指针指向原型。
原型链: 实例与原型的链条。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型链</title> </head> <body> <script> function Humans() { this.foot=2; } Humans.prototype.getFoot=function () { return this.foot; } function Man() { } Man.prototype = new Humans(); //改变原型指向(继承Humans对象) var man1 = new Man(); alert(man1.getFoot()); alert(man1 instanceof Humans); </script> </body> </html>
原型链问题 如下实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型链</title> </head> <body> <script> function Humans() { this.name="张三"; this.fruit=["香蕉","苹果","梨"]; } function Man() { } Man.prototype = new Humans(); //继承Humans var man1 = new Man(); man1.fruit.push("火龙果"); alert(man1.fruit); var man2 = new Man(); alert(man2.fruit); /* 问题1:引用类型,原型属性被所有的实例对象共享了。 问题2:创建子类的时候 不能给父类传参数了。 * */ </script> </body> </html>
解决方式:使用借用构造函数继承 如下解决实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>借用构造函数</title> </head> <body> <script> //问题1:引用类型,原型属性被所有的实例对象共享了。 //解决问题1 function Humans() { this.name="张三"; this.fruit=["香蕉","苹果","梨"]; } function Man() { Humans.call(this); //把this 替换为 Humans对象。 让每一个Man对象都拥有一个独立的fruit空间。 } // Man.prototype = new Humans(); //继承Humans var man1 = new Man(); man1.fruit.push("火龙果"); alert(man1.fruit); var man2 = new Man(); alert(man2.fruit); </script> </body> </html>
图中 call方法可以 把this 替换为 Humans对象 如果Humans对象有参数 也可以 把 Humans对象 参数传递过来,
语法:对象.call(this,参数......);
优点:解决了属性继承,并且值不重复的问题。
缺陷:父级类别中的方法不能继承。
解决方法:组合继承 : 组合继承就是 原型继承+借用继承。
实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>组合继承</title> </head> <body> <script> function Humans(name) { this.name=name; this.fruit=["香蕉","苹果","梨"]; } Humans.prototype.getName=function () { return this.name; } function Man(name) { //继承Humas ,同时传递了一个name参数 Humans.call(this,name); //继承属性 this.head=1; } Man.prototype = new Humans(); //继承方法 Man.prototype.getHead=function () { return this.head; } var man1 = new Man("李四"); man1.fruit.push("火龙果"); alert(man1.fruit); alert(man1.getName()); alert(man1.getHead()); var man2 = new Man("王五"); alert(man2.fruit); alert(man2.getName()); alert(man2.getHead()); </script> </body> </html>