JavaScript의 객체지향 프로그래밍 소개
이 글의 내용 순서:
1. 객체 개념 소개, 2. 객체 지향 프로그래밍의 특징, 3. JavaScript에서 클래스 및 인스턴스 객체 생성, 5. 프로토타입 API; 프로토타입 객체의 구체적인 사용 7. 상속을 달성하기 위한 프로토타입 객체의 사용에 대한 심층적인 이해 8. 다형성: 재정의 - 재작성
1. 객체 개념 소개:
객체 지향 프로그래밍. 먼저 객체가 무엇인지 이해해야 합니다. 객체의 개념은 참조 데이터 유형에서도 설명되므로 처음에는 객체의 개념을 먼저 소개하겠습니다. [객체——객체]:
객체란 무엇입니까? : 객체는 사물의 속성과 기능을 저장하는 저장 공간입니다. 이름을 만들 때
객체 사용 시기: 사물의 속성과 기능이 중앙에 저장되는 한; : (3가지 방법)
첫 번째 은 객체를 사용하는 것입니다. 직접 방법 객체 생성 및 객체 멤버 초기화: var obj={
name:tom,
age:25,
gender:M,
intro:function (){
alert("my name is tom")
}
}
두 번째 는 new 키워드를 사용하는 것입니다. 빈 개체를 만든 다음 개체에 멤버를 추가하려면:
var boj=new Object(); obj.name="tom"; obj.age=25; obj.gender="M"; obj.intro=function(){ alert("my name is tom"); }
세 번째 는 생성자를 사용하는 것입니다. : 여기서 생성자를 조금 설명하겠습니다.
What 생성자: 객체 클래스의 동일한 구조를 설명하는 함수입니다.
이유: 객체 리터럴을 사용하여 객체를 생성합니다. 동일한 코드가 많이 생성되고 코드 중복이 유지 관리가 불편합니다. 동일한 구조를 가진 여러 객체가 반복적으로 생성되는 한
기능: 객체를 생성하고 속성 값을 저장합니다(요구 사항 구조에 빈 객체 구성). JavaScript의 객체는 연관 배열입니다. 객체의 구성원에 액세스하려면
.및 [ ]를 사용하고, 액세스된 속성 이름이 고정된 경우
.를 사용하여 액세스합니다. 따라서 할당 작업에서 속성 이름이 없으면 새 속성을 추가해도 오류가 보고되지 않습니다.
obj['property name'] / obj['method name']( ) ;
상속 : 상위 객체의 멤버, 하위 객체는 생성 없이 직접 사용할 수 있습니다. (이유: 코드 재사용, 메모리 절약, 시기: 동일한 유형의 여러 하위 객체가 동일한 멤버를 갖는 한, 방법: JS의 상속이 구현됩니다. 프로토타입 객체를 통해;) 다형성: 동일한 것이 상황에 따라 다른 상태를 보여줍니다. 재정의만 지원하는 JS 다형성: 하위 객체가 상위 객체의 멤버를 사용하기 쉽지 않다고 느끼는 경우 자식 개체에 부모 개체의 멤버와 동일한 이름을 가진 자체 멤버를 정의하면 재정의 결과는 다음과 같습니다. 이 멤버를 다시 호출하면 부모 개체의 공유 멤버 대신 자체 멤버를 사용하는 데 우선순위가 부여됩니다.
3. JavaScript 객체 지향 프로그래밍 - 클래스 및 인스턴스 객체 생성
JavaScript는 객체 지향 프로그래밍 언어가 아니라 객체 기반 프로그래밍 언어입니다. ; 객체 지향의 매우 분명한 상징은 [클래스] 개념입니다. 예를 들어 Java 언어는 객체 지향이며 자체적으로 클래스를 갖습니다. 클래스 개념은 동일한 속성과 메소드를 가진 특정 객체를 얼마든지 생성할 수 있습니다. 그러나 JavaScript에는 클래스 개념이 없습니다. 코드를 사용하여 동일한 구조를 구성한 다음 이 모의 클래스를 사용할 수 있습니다.
在开篇我们讲述了关于对象的实例,我们说面向对象就是可以通过一个类实例化出任意多个具有相同属性和方法的对象,那么JavaScript既然是基于对象的语言没有类的概念,我们是怎么通过代码模仿一个这样相同结构的类呢?有俩种方式,第一种就是工厂模式,我们知道工厂是可以生产出任意多款独立的产品,比如说生产某一款玩具他可以生产出很多个不同种类的玩具还有衣服工厂可以生产出任意不同款式的衣服,那么我们构造类的工厂模式也是类似的思维逻辑;第二种就是构造函数模式(默认标准模式);
在第二个序号下我们讲述了面向对象程序设计的三大特点:封装、继承和多态;简单来讲声明一个对象并定义对象成员就是封装,所以接下来的模拟类的创建和实例对象的创建都体现了封装这一概念;下面体会一下封装,封装的定义查看目录序号2;
第一工厂模式:
创建一个工厂(函数),首先创建一个普通的函数,函数名就是类型名,在函数里面创建一个模板对象,并且添加相应的对象成员(对象的属性和方法称之为对象的成员),最后返回模板对象;即通过一个函数,在函数内部创建了一个模板对象,最后函数返回模板对象,外部通过传入不同的参数使用不同的变量去调用函数就会得到不同的结果;PS:调用函数的不同变量就是实例化的不同对象;
function cellPhone(material,ms,mpx,cpu,ramc){ var obj=new Object(); obj.material=material ; //主屏材料 obj.ms=ms ; //主屏尺寸 obj.mpx=mpx ; //主屏分辨率 obj.cpu=cpu ; //CPU型号 obj.ramcl=ramc ; //RAM容量 return obj; } var c1=cellPhone('IPS',4.3,'1280*720','高通骁龙','2GB'); var c2=cellPhone('IPS',5,'1200*620','高通骁龙','4GB');
第二构造函数:
通过构造函数的形式去调用,约定成俗函数名第一个字母大写就是一个类型名,形参列表定义参数必须由外部传入才能运行时的属性值,使用this关键字绑定属性到调用函数者的对象上(this是函数作用域中自动定义的引用函数调用时 · 前的对象的AO中的一个关键词默认创建);模板构建好以后再构建一个对象去使用模板内容——构建一个对象通常有几个步骤,具体几个呢?使用new关键字,参数传递,使用模板内容和返回对象!
function Student(name,age,gender,class){ this.name=name; this.age=age; this.gender=gender; this.class=class; this.intro=function(){ alert("我的名字叫"+name+"我来自"+class+"班"); } } var s1=new Student('小红',12,'FM','4');//创建了一个对象 小红 var s2=new Student('小明',13,'M','4'); //创建了一个对象 小明
在上述代码中new做了4件事:1、创建一个新的对象,并将this自动指向新对象 ; 2、让新对象继承构造函数的原型对象;(下一部分讲解原型对象)3、执行构造函数中的语句,并向新对象中添加新成员并赋值;4、返回新对象的地址,保存到变量中;
下面通过这一段代码来验证JavaScript中使用代码来模仿的类:
1function Student(name,age,gender,class){ this.name=name; this.age=age; this.gender=gender; this.class=class; this.intro=function(){ alert("我的名字叫"+name+"我来自"+class+"班"); } } 11var s1=new Student('小红',12,'FM','4');//创建了一个对象 小红 12var s2=new Student('小明',13,'M','4'); //创建了一个对象 小明 alert(s1==s2); //false 验证了根据不同的模板创建出来不同的实例对象; //alert(s1.constructor == Student); 返回true ; 表示s1的构造器是Student吗?结果时true; //alert(s2.constructor == Student); 返回true ; 表示s2的构造器是Student吗?结果时true; alert(p1 instanceof Student);//true 表示p1是Student的实例,返回false就说明不是; alert(p1 instanceof Object);//true 表示p1是Student的实例,返回false就说明不是;
通过以上一段代码使用三种方法验证了面对对象的类的概念,每一个实例对象都是基于一个相同的模板(构造函数)独立创建出来的对象;
构造函数三种创建实例对象的方法:
1、使用new关键字:var 变量名=new 类型名/构造函数名(属性值列表);
2、把构造函数当做普通函数调用:类型名/构造函数名(属性值列表); //当做一个普通函数调用,此时构造函数是一个全局的函数,在全局环境里定义属性并赋值 直接定义在window上了,this指向也是window了;
3、在另一个对象的作用域中调用:使用call强制替换this的指向,比如现在有新新对象var a=new Object();a对象里没有添加任何成员,现在要在新的对象a的作用域中调用上一段中我们创建的Student构造函数(类),具体操作如下:
Student.call(a,'大明','16','M','1');
此时就可以在对象a中访问name、age、gender、class等属性了;
alert(a.name); //大明
以上我们讲解了2种创建构造函数(类)和3种通过构造函数(类)创建实例对象的方法;那么构造函数(模仿的类)在直接使用中有什么弊端吗?来通过下面这短代码观察一下构造函数(模仿的类)在直接使用过程中有什么弊端。
function Student(name,age,gender,class){ this.name=name; this.age=age; this.gender=gender; this.class=class; this.intro=function(){ alert("我的名字叫"+this.name+"我来自"+class+"班"); } } var s1=new Student('小红',12,'FM','4');//创建了一个对象 小红 var s2=new Student('小明',13,'M','4'); //创建了一个对象 小明 alert(s1.name==s2.name); //false alert(s1.intro==s2.intro); //false
通过上述代码,我们可以看到将方法和属性都定义在构造函数中,每创建一个新的对象时,都会为每个对象创建相同方法和属性的副本,这样就会有大量重复相同的代码出现,浪费内存影响代码的执行效率,我们每创建一个实例对象执行的属性和方法是相同的所以没有必要每次都去创建一个构造函数中想同方法和属性的副本。到这里有一些聪明的人就发现了,他把所有的属性和功能方法都放在构造函数外,在构造函数里以引用的方式去使用这些属性和方法,这些属性和方法就被公共出来了,那么问题又来了全局变量容易被污染或者篡改不安全还一直占用内存!显然这样不合适,那么有一种方法能够让这些共同的代码被共用并且还不是全局的办法吗?有的,在函数中是有的,那就是函数的prototype属性;
封装的问题:将方法和属性定义在构造函数中,通过这个构造函数创建的所有的实例对象多会为他们创建相同的属性和方法的副本,浪费内存;【解决:使用继承,JS中的继承都是通过原型对象实现的】
所以我们引出了下一个话题就是:原型对象和继承;
4、原型概念
我想之前有很多人知道原型这个概念,但是不清晰怎么去使用原型和只知道原型这样一个名词的大有人在;原型是通过一个函数的prototype属性指向的一个空对象——原型对象;那么原型对象在面向对象编程中究竟扮演着一种什么样的角色,原型对象、构造函数和实例对象有着怎样的三角关系?且听郭某给大家慢慢说来;我也希望大家能通过我的这一段文字和代码对于原型对象有一个更加深入的理解;
原型=>prototype,原型是关于继承的,放在原型对象里的属性和方法是可以被由本构造器下所有实例对象所共享的,所以原型对象也可以理解成是集中存储同一类型所有子对象共有成员的的父对象;创建函数的时候都有一个prototype属性,这个属性其实就是一个指针,总是指向一个对象,这个对象的用途就是将你特定的属性和方法包含在内让所有实例对象共享的作用!所以这样就解决了上述通过构造函数法每创建一个新对象就重新复制一套属性和方法,这样放在同一个构造函数的prototype指向的对象里的所有的属性和方法,可以被所有使用本构造函数创建出来的实例对象共享;
//创建一个构造函数Student,保存的学生是同一个班级相同性别具有相同功能的*; function Student(name,age){//name和age不同每次都需要重新创建 this.name=name; this.age=age; } //每个函数都有一个prototype属性,这个属性总指向一个对象; var obj=Student.prototype;//这里的obj 就是名为Student函数的prototype属性指向的函数 //把所有相同的属性和方法共享出来; obj.gender='男'; obj.class=4; obj.intro=function(){ alert("我的名字叫"+name+"我来自"+class+"班"); } var s1=new Student('小红',12);//创建了一个对象 小红 var s2=new Student('小明',13); //创建了一个对象 小明 alert(s1.gender==s2.gender); //true alert(s1.intro==s2.intro); //true /*放在构造函数.prototype属性里的属性和方法可以共享不需要每一次重新创建,提高代码执行的效率*/
所以就上面的代码我们可以看出来,就是new出来一百万个对象放在函数prototype属性指向的对象我们JavaScript称其为原型对象里的共同属性和方法只有一个,也就是放在原型对象里的所有方法和属性被new出来的一百万个实例对象共享了;我的天呐,这得节约多少代码啊~~~~~~所以这样一来就把直接调用构造函数创建对象的弊端给解决掉了。下面我用一张图具体来标识一下构造函数、原型对象和实例对象之间的关系;
所以构造函数、原型对象 、实例对象之间就有如下表达式:
构造函数.prototype = 原型对象
原型对象.constructor = 构造函数
实例对象.prototype = 原型对象
5、原型对象API:
.isPrototypeOf(Object): 用于判断传入的对象是否是另外一个对象的原型。=>alert(obj.isPrototypeOf(s1));表述的意思就是s1的原型对象是obj吗?
.hasOwnProperty();用于判断一个对象属性是自有属性吗? 可用此方法判断属性是属于自己的还是继承自原型对象的;=>obj.hasOwnProperty('name');obj里有name属性吗?有返回true,没有返回false;
.in 用于判断属性是否存在于 实例对象和原型对象中;不管是存在于实例对象中还是原型对象中只要当前对象能访问到就返回true;'name' in s1;=>只要在s1中或者s1的原型对象中任一个有就返回true;
Object.getPrototypeOf(); 由实例对象获得原型对象;var prototypeObj = Object.getPrototypeOf(s1);=>获取实例对象s1的原型对象,(本例中获取完成以后用一个变量接收了,也可以使用链式操作直接使用);
Object.keys(); 获取到当前对象里的所有keys(属性名) 返回一个数组;
Object.getOwnPropertyNames(); 遍历对象所有的属性(不论该内部属性能否被遍历),返回一个包含所有遍历到属性的一个数组;var attributes = Object.getOwnPropertyNames(Student.prototype);
6、原型对象的具体使用:
原型对象在JS中有俩大主要的作用:第一就是实现继承,另外一个作用就是扩展对象中的属性和方法:
下面通过实现一个自定义遍历多维数组的方法来体会原型对象是怎么去给对象扩展属性和方法的;我们知道ECS5针对数组API提出了一个遍历数组的新方法就是arr.forEach(function(val,i,arr){//val:值;i:下标;arr:数组变量名}),这个方法可以对数组内每个元素执行相同的操作,但是它值能适用与一维数组,多维数组遍历它会把大于一维数组的数组元素作为一个整体拿出来;那么下面通过向Array原型对象里扩展一个自定义的可以遍历多维数组的each方法;
// 向Array里添加 each方法 // ECMA5 forEach 循环遍历数组的每一项(只适用于遍历一维数组) /* var arr = [1,2,3,[a,b,c]4,5]; arr.forEach(function(val , i , arr){ alert(val); }); //查看弹出结果的不同 */ // 自己向Array里添加一个 each方法 能遍历多维数组 var arr = [1,2,3,[4,[5,[6]]]]; // arr.length Array.prototype.each = function(fn){ try{ //1 遍历数组的每一项x需要一个计数器记录当前遍历的元素位置 this.i || (this.i=0); //使用自定义属性和短路逻辑检测并初始化一个计数器=>var i = 0 ; //2 判断什么时候去走each核心方法 // 当数组的长度大于0的时候 && 传递的参数必须为函数 if(this.length >0 && fn.constructor == Function){ // 循环遍历数组的每一项 while(this.i < this.length){ //while循环的范围 //获取数组的每一项 var t = this[this.i]; //如果获取到当前元素是一个数组 if(t && t.constructor == Array){ // 直接做递归操作 t.each(fn); } else { //如果不是数组是一个单个元素 // 这的目的就是为了把数组的当前元素传递给fn函数 并让函数执行 //fn.apply(t,[t]); fn.call(t,t); } this.i++ ; } his.i = null ; // 释放内存 垃圾回收机制回收变量 } } catch(err){ // 错误提醒 } return this ; } arr.each(function(val){ alert(val); 46});
简单原型使用:
之前我们已经学过了原型如何使用,那么现在我们介绍一种简单原型的使用方式:即直接通过对象直接量来重写整个原型对象(这种方法会改变原型对象的构造器);按照惯常的方法就是我们创建一个构造函数,然后通过构造函数的prototype的属性来为原型对象添加属性和方法,每次只能添加一个属性或者方法,操作起来有一些繁琐,那么简单原型只用就是把一个空对象或者是已经封装好要添加给原型对象的一些属性和方法的匿名对象赋值给这个构造函数的原型对象上,注意在这里执行的是赋值操作很显然这个匿名的对象的父类是一个Object,所以就有:构造函数类型名.prototype.constructor=>function Object(){[native code]} ; 所以简单原型使用方法就改变了构造函数的原型对象的构造器;那么怎么去解决这个问题呢?我们可以在简单原型方法封装匿名对象时首先添加一个属性constructor并赋值为构造函数的名称(constructor:构造函数的类型名);我们知道在对象中构造器(constructor)属性是不可以被遍历到的,如果这样我们手动添加了constructor属性原型对象的构造器是保持一致了但是【问题来了】在这里的constructor属性是可以被遍历到的;【解决】ES5中的Object.defineProperty();方法可以为原型对象重新加入并设置构造器属性。(注意:因为是ES5的新特性所以本方法对于火狐4和IE8以前的浏览器是不被支持的;);本方法有3个参数分别是:参数1 :重设构造器的对象 ;参数2:设置什么属性 ; 参数3:options配置项(通常是一个对象);
function Student(){ } Student.prototype = { //constructor : Student , //重新校正原型对象的构造器,此情此景下的constructor属性是可以被遍历到的; name: '小明' , age : 12 , gender : '男' , intro : function(){ alert('我是小明,我喜欢小红!'); } }; // ECMAScript5 提供给原型对象重新设置构造器的方法 Object.defineProperty(); // 3个参数 参数1 :重设构造器的对象 参数2:设置什么属性 参数3:options配置项 /*Object.defineProperty(Student.prototype , 'constructor' , { enumerable : false , //设置属性不可被遍历 value : Person }); */ var s1 = new Student(); //alert(s1.name); //s1.intro(); alert(Student.prototype.constructor); // Student // 遍历对象的keys,使用第一种方法constructor是可以被遍历到的,使用ES5特性添加是不可以被遍历到的; for( attr in s1){ alert(attr); }
原型的动态性(注意原型和创建实例的先后顺序)——>涉及到一个原型链的概念;
原型链:由多级父元素连续继承形成的链式结构,保存着所有的属性和方法,控制着成员的使用顺序,沿原型链自底向上依次查找使用;
原型链 VS 作用域链
作用域链:保存着所有变量,控制着变量的使用顺序,沿作用域链自上向下查找使用;
我们知道JavaScript本身有一个特性就是解释执行即从上到下一行一行解析执行操作;我们还知道在JavaScript的对象中实例对象和原型对象是有继承关系的;所以就有原型对象的定义必须要先于实例对象的创建而创建,否则就不能使用到原型对象里的方法和属性了;所以简单原型的使用顺序必须是先定义远行对象然后再new实例对象!
PS:原型对象的创建一定是先于实例对象的创建的;
function Student(){ } var s1 = new Student(); // 此时实例s1的原型对象就是一个空对象=>{} //---------------------------------------------------------------------- //在这里使用简单原型定义原型属性和方法实际上他的构造器属性已经发生了改变,构造器变成了Object(实质上就切断了Student和Student.prototype的关联关系了); //我们为了一致又自己手动添加了一个constructor属性和其他需要的属性,所以在此之前实例的s1的原型对象是没有这些属性; Student.prototype = { constructor : Student , //校正原型对象的构造器 intro: function(){ alert('我是小明,我喜欢小红!'); } }; //s1.intro(); // error 因为原型对象里面没有任何属性和方法 // 注意简单原型使用的顺序(实例对象必须在原型对象之后创建) var s2 = new Student(); s2.intro();//我是小明,我喜欢小红!
原型对象中常用的开发模式:
代码中介绍三种常用方式:
// 混合使用原型和构造函数式 (模拟定义一个类即构造函数 开发时最常用的方式) /* function Person(name , age , friends , job){ this.name = name ; this.age = age ; this.friends = friends ; this.job = job ; } Person.prototype = { constructor: Person , sayName : function(){ alert(this.name); } }; var p1 = new Person('z3' , 20 , ['王五','赵六'] , '技术总监'); var p2 = new Person('李四', 25 , ['王五','赵六' ,'赵7'] ,'boss'); alert(p1.friends); p1.sayName(); alert(p2.friends); p2.sayName(); */ //动态原型模式:(所有的代码 都封装到一起) /* function Person(name , age , friends , job){ this.name = name ; this.age = age ; this.friends = friends ; this.job = job ; //动态原型方法: if( typeof this.sayName != 'function'){ Person.prototype.sayName = function(){ alert(this.name); } } } */ //稳妥构造函数式: durable object(稳妥对象) 非常安全的环境中 //特点: 1 、没有公共属性 , 2 、不能使用this对象 /* function Person(name , age , job){ // 创建一个要返回的对象 var obj = new Object(); //可以定义一下私有的变量和函数 private var name = name ; //var sex = '男'; //var saySex = function(){}; //添加一个方法 obj.sayName = function(){ alert(name); } return obj ; } var p1 = new Person('张三'); p1.sayName(); */
7、深入理解使用原型对象实现继承:
1、使用prototype实现继承——原型继承
参数要传递给被继承原型对象的父类实例中;
function A(){...} A.prototype = { a : "a", sayA : function(){ console.log("this is a."); } } // 1 创建A()构造器的对象 var a = new A(); function B(){ this.b = "b"; this.sayB = function(){ console.log("this is b."); } } // 1 将B.prototype指向A()构造器创建的对象 //B.prototype = a;//显然此时B的原型对象将包含一个指向A原型的指针,相应的A原型中也包含着一个指向A构造函数的指针。 // 2. 将B.prototype指向A.prototype B.prototype = A.prototype; var b = new B(); console.log(b.b); b.sayB(); console.log(b.a); b.sayA();
2、类继承(借用构造函数继承)有时候也管他叫call和apply继承——只继承构造函数不继承原型对象;
function A(){ this.a = "a"; this.sayA = function(){ console.log("this is a."); } } function B(){ // 调用A.call(this)方法,实现继承 A.call(this); this.b = "b"; this.sayB = function(){ console.log("this is b."); } } var b = new B(); console.log(b.b); b.sayB(); console.log(b.a); b.sayA();
3、原型继承+借用构造函数继承 = 混合继承// 父类
function Person(name, age){ this.name = name ; this.age = age ; } // 父类的原型对象属性 Person.prototype.id = 10 ; Person.prototype.sayName = function(){alert(this.name);}; // 子类 function Boy(name , age , sex){ // call apply Person.call(this,name,age); // 1 借用构造函数继承 继承父类的模版 this.sex = sex ; } // 2 原型继承 // 只剩下 父类的实例 和 父类的原型对象的关系了 Boy.prototype = new Person(); //继承父类的原型对象 var b = new Boy('李四' , 20 , '男'); alert(b.name); alert(b.sex); b.sayName();
在混合继承中我们知道在原型继承的这一步只是没有传递参数,没有执行构造函数罢了,其实同样是继承了构造函数和原型对象,那么在混合继承中就做了三件事:继承了俩遍构造函数和继承了一遍原型对象,【问题来了】既然构造函数已经被继承过一遍了能不能再只继承一遍原型对象呢?【解决】自定义一个方法只继承原型对象;
function extend(child ,father){ // 目的: 实现只继承父类的原型对象 var F = new Function(); // 1 创建一个空函数 目的:空函数进行中转 F.prototype = father.prototype; // 2 实现空函数的原型对象和超类的原型对象转换 child.prototype = new F(); // 3 原型继承 child.prototype.constructor = child ; // 还原子类的构造器 //保存一下父类的原型对象: 一方面方便解耦 另一方面方便获得父类的原型对象 child.superClass = father.prototype; //自定义一个子类的静态属性 接受父类的原型对象 //判断父类的原型对象的构造器 (加保险) if(father.prototype.constructor == Object.prototype.constructor){ father.prototype.constructor = father ; //手动欢迎父类原型对象的构造器 } } // 混合继承:原型继承和借用构造函数继承 function Person( name , age){ this.name = name ; this.age = age ; } Person.prototype = { constructor: Person , sayHello: function(){ alert('hello world!'); } }; function Boy(name , age , sex){ //call 绑定父类的模版函数 实现 借用构造函数继承 只复制了父类的模版 Boy.superClass.constructor.call(this , name , age); this.sex = sex ; } //原型继承的方式: 即继承了父类的模版 又继承了父类的原型对象 //Boy.prototype = new Person(); // 只继承一遍父类的原型对象 extend(Boy , Person); // 给子类加了一个 原型对象的方法 Boy.prototype.sayHello = function(){ alert('hi javascript!'); } var b = new Boy('张三' , 20 , '男'); alert(b.name); alert(b.sex); //b.sayHello(); Boy.superClass.sayHello.call(b); //alert(Boy.superClass.constructor); // 混合继承的缺点: 3件事 : 继承了父类的2次模版 , 继承了一次父类的原型对象 // extend方法 2件事: 继承1次父类的模版 继承一次父类的原型对象
8、多态:override—重写
在上一个自定义只继承原型的例子里,代码41到43行就是一个重写,自定义属性superClass就是保存了父类的原型对象确保在重写之后还能很便捷的使用到父类的原型对象成员;
위 내용은 JavaScript의 객체지향 프로그래밍 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

주식 분석을 위한 필수 도구: PHP 및 JS에서 캔들 차트를 그리는 단계를 배우십시오. 인터넷과 기술의 급속한 발전으로 주식 거래는 많은 투자자에게 중요한 방법 중 하나가 되었습니다. 주식분석은 투자자의 의사결정에 있어 중요한 부분이며 캔들차트는 기술적 분석에 널리 사용됩니다. PHP와 JS를 사용하여 캔들 차트를 그리는 방법을 배우면 투자자가 더 나은 결정을 내리는 데 도움이 되는 보다 직관적인 정보를 얻을 수 있습니다. 캔들스틱 차트는 주가를 캔들스틱 형태로 표시하는 기술 차트입니다. 주가를 보여주네요

얼굴 검출 및 인식 기술은 이미 상대적으로 성숙하고 널리 사용되는 기술입니다. 현재 가장 널리 사용되는 인터넷 응용 언어는 JS입니다. 웹 프런트엔드에서 얼굴 감지 및 인식을 구현하는 것은 백엔드 얼굴 인식에 비해 장점과 단점이 있습니다. 장점에는 네트워크 상호 작용 및 실시간 인식이 줄어 사용자 대기 시간이 크게 단축되고 사용자 경험이 향상된다는 단점이 있습니다. 모델 크기에 따라 제한되고 정확도도 제한됩니다. js를 사용하여 웹에서 얼굴 인식을 구현하는 방법은 무엇입니까? 웹에서 얼굴 인식을 구현하려면 JavaScript, HTML, CSS, WebRTC 등 관련 프로그래밍 언어 및 기술에 익숙해야 합니다. 동시에 관련 컴퓨터 비전 및 인공지능 기술도 마스터해야 합니다. 웹 측면의 디자인으로 인해 주목할 가치가 있습니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

인터넷 금융의 급속한 발전으로 인해 주식 투자는 점점 더 많은 사람들의 선택이 되었습니다. 주식 거래에서 캔들 차트는 주가의 변화 추세를 보여주고 투자자가 보다 정확한 결정을 내리는 데 도움이 되는 일반적으로 사용되는 기술적 분석 방법입니다. 이 기사에서는 PHP와 JS의 개발 기술을 소개하고 독자가 주식 캔들 차트를 그리는 방법을 이해하도록 유도하며 구체적인 코드 예제를 제공합니다. 1. 주식 캔들 차트의 이해 주식 캔들 차트를 그리는 방법을 소개하기 전에 먼저 캔들 차트가 무엇인지부터 이해해야 합니다. 캔들스틱 차트는 일본인이 개발했습니다.

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

js와 vue의 관계: 1. 웹 개발의 초석인 JS 2. 프론트엔드 프레임워크로서의 Vue.js의 등장 3. JS와 Vue의 상호 보완적인 관계 4. JS와 Vue의 실제 적용 Vue.
