摘要:在创建子类实例时,不能向超类型的构造函数中传递参数。构造函数继承子类传进的值是基本思想是在子类构造函数的内部调用超类或父类型构造函数。继承保证构造函数指针指向如果想同时继承多个,还可使用添加属性的方式类继承,
OOP:Object Oriented Programming 面向对象编程。
题外话:面向对象的范围实在太大,先把这些大的东西理解理解。1.什么是对象?
根据高程和权威指南上的定义,对象是一组没有特定顺序的值,我们可以把对象看成是从字符串到值的映射。2.怎样理解原型和原型链?
原型:
根据权威指南上的定义:每一个js对象(null除外)都和另一个对象相关联, “另一个”对象就是我们熟知的原型,每一个对象都从原型上继承属性。原型也是对象。 通俗点讲,就是一个拥有prototype属性、且这个属性指向函数的原型对象的对象。
原型的好处就是:原型上的方法和属性会被所有实例所共享。
原型链:
当访问某个实例属性或方法时,会先自身对象中查找,查不到时再往当前对象原型上找; 若依然没找到,则会继续往原型对象的原型上找,一直到找到结果或者找到Object.prototype为止也没找到, 然后这时就会返回undefined,这么一个链式查找过程形成的结构就叫原型链。
每个对象都有一个__proto__属性,函数也是对象,所以函数也有;3.面向对象的三大特性是什么?
每个函数都有一个prototype属性,而实例对象没有。
封装,继承,多态(指一个方法可以有多种调用方式:例如有参或无参)4.创建对象有哪些方式? (1).对象字面量
let obj={};
(2).Object方式let obj=new Object();
(3).Object.createlet obj=Object.create({}/null);
注意!!前面这三种方式的的构造函数都是Object,而Object已经是原型链的最顶端了,所以Object.prototype都为undefined。(4).工厂模式可以用实例的__proto__.constructor查看构造函数是否指向Object构造函数。
function person(name,job){ let o={}; o.name=name; o.job=job; o.sayName=function(){ console.log(this.name); } return o; } let p1=person("nagi","sleep"); console.log(p1.constructor); // 指向Object p1 instanceof person; // false;
优缺点:
这种模式虽然解决了量产对象的问题,但却无法获知当前对象是何类型 (例如类型:Array,Math等内置对象,或者BOM(window)/DOM的宿主对象,又或者自定义对象等)
注意点:函数首字母不用大写。
(5).构造函数模式function Person(name,job){ this.name=name; this.job=job; this.sayName=function(){ console.log(this.name); } } let p1=new Person("nagi","sleep"); console.log(p1.constructor); // 指向Person p1 instanceof Person; // true
与工厂模式区别:
a.没有显式创建对象; b.直接将属性和方法赋给了this对象 c.不有return语句;
拓展:new操作符做了些什么?
a.创建一个新对象; b.将构造函数的作用域赋给新对象(因此this就指向一这个新对象); c.执行构造函数中的代码(为这个新对象添加属性); d.返回新对象(默认返回当前对象,除非有显示返回某个对象)
优缺点:
首先是解决了工厂模式中不能判断类型的问题; 但缺点是每实例一次的同时还会把方法重新创建一遍,造成内存资源浪费; 其次,因构造函数与其它函数的唯一区别就是调用方式不一样,所以当被当作普通函数调用时,其内部的this会指向全局,引发作用域问题。(6).原型模式
function Person(){}; // 写法一: Person.prototype.name="nagi"; Person.prototype.job="sleep"; Person.prototype.sayName=function(){ console.log(this.name); }; // 写法二:注意,这种直接更改原型指向的写法,会改变constructor指向,指向Object构造函数 /* Person.prototype={ // constructor:Person, name:"nagi", job:"sleep", sayName:function (){ console.log(this.name) } }*/ let p1=new Person();
优缺点:
优点:解决了上述构造函数的缺点,原型对象上的属性和方法为所有实例所共享。 缺点: a.缺点也是所有实例共享方法和属性,因此其中一个实例更改了引用类型的属性值时,其他的实例也会被迫改变。(属性) b.默认情况下所有实例都取得相同的属性值。(7).组合模式(结合构造函数和原型模式)
function Person(name,job){ this.name=name; this.job=job; } Person.prototype.sayName=function(){ console.log(this.name); } let p1=new Person("nagi","sleep");4.对象继承方式有哪些? (1).原型链继承
function Parent(){ this.name="nagi"; this.colors=["red","blue","green"]; } Parent.prototype.sayName=function(){ console.log(this.name); } function Child(){ this.job="sleep"; }; Child.prototype=new Parent(); // 要注意:这种重写原型链的写法是会切断构造函数与最初原型之间的联系的, // 意味着此时Child.prototype.constructor指向Parent var child=new Child();
这种方式的基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
问题点:
a.上述例子中,通过原型继承方式继承相当于专门创建了一个Child.prototype.colors的属性, 因为引用类型的原型属性会被所有实例共享,也就意味着Child的所有实例都会共享colors这一属性, 当其中一实例修改它的值时,那么其他的实例的值也会跟着被改变。 b.在创建子类实例时,不能向超类型的构造函数中传递参数。鉴于此,实际很少多带带用原型链继承。
超类型:比如Child类继承了Parent类的属性,那么Parent就是Child的超类(也叫父类)。(2).构造函数继承(call/apply)
function Parent(name){ console.log(`子类传进的name值是:${name}`) this.name="nagi"; this.colors=["red","blue","green"]; } Parent.prototype.sayName=function(){ console.log(this.name); } function Child(){ Parent.call(this,"Bob") this.job="sleep"; }; var child1=new Child(); var child2=new Child();
基本思想是在子类构造函数的内部调用超类(或父类)型构造函数。
优缺点:
优势:解决了原型链的两个问题; 缺点: a. 方法都在构造函数定义的话,那函数复用就无从谈起了。 b. 父类在原型中定义的方法,对于子类型来说是不可见的。鉴于此,构造函数也很少用。[捂脸](3).组合继承(即原型链+构造函数)
function Parent(name){ this.name=name; this.job="sleep"; this.colors=["red","blue","green"]; } Parent.prototype.sayName=function(){ console.log(this.name); } function Child(name){ Parent.call(this,name); this.job="eat"; } Child.prototype=new Parent(); Child.prototype.constructor=Child; let child1=new Child("nagi"); let child2=new Child("Bob");
其实现思路是用原型链实现对原型属性和方法的继承,而借助构造函数实现对实例属性的继承。 优点:在前两者基础上,补足了构造函数和原型链的缺点,是比较常用的方式。(4). Object.create继承
function Parent(name){ this.name=name; this.job="sleep"; this.colors=["red","blue","green"]; } Parent.prototype.sayName=function(){ console.log(this.name); } function Child(){ Parent.call(this); // 保证构造函数指针指向Child } Child.prototype=Object.create(Parent.prototype,{name:{value:"nagi"},job:{value:"eat"}}) // 如果想同时继承多个,还可使用Object.assign()添加属性 // Object.assign(A.prototype, B.prototype); let child=new Child();(5)ES6的extends方式
类继承,class A extends B
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/99321.html
摘要:构造函数通常首字母大写,用于区分普通函数。这种关系常被称为原型链,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。中所有的对象,都有一个属性,指向实例对象的构造函数原型由于是个非标准属性,因此只有和两个浏览器支持,标准方法是。 从这篇文章开始,复习 MDN 中级教程 的内容了,在初级教程中,我和大家分享了一些比较简单基础的知识点,并放在我的 【Cute-JavaScript】系...
摘要:组合构造原型模式将自身属性于构造函数中定义,公用的方法绑定至原型对象上原型对象的解释每一个函数创建时本身内部会有一个固有的原型对象,可以通过函数名去访问,而其原型对象又有一个属性指针指向该函数。 每次遇到JS面对对象这个概念,关于继承及原型,脑海里大概有个知识框架,但是很不系统化,复习下,将其系统化,内容涉及到对象的创建,原型链,以及继承。 创建对象 两种常用方式,其余的比较少见工厂模...
摘要:所以继承了对象的所有方法,当你用时,会先查一下它的构造函数的原型对象有没有有方法,没查到的话继续查一下的原型对象有没有这个方法。 普通函数与构造函数的区别 在命名规则上,构造函数一般是首字母大写,普通函数遵照小驼峰式命名法。 在函数调用的时候: function fn() { } 构造函数:1. new fn( ) 2 .构造函数内部会...
阅读 2283·2021-11-15 11:37
阅读 2953·2021-09-01 10:41
阅读 786·2019-12-27 11:58
阅读 747·2019-08-30 15:54
阅读 714·2019-08-30 13:52
阅读 2930·2019-08-29 12:22
阅读 1074·2019-08-28 18:27
阅读 1451·2019-08-26 18:42