JS高级知识(面向对象,原型,原型理解,继承)
相关知识点概念
- 函数——相当于工具、面向对象就是函数,函数也叫方法
- 对象——相当于工具包、对象包括函数和属性
- 整个js都是面向对象的
- 对象有属性和方法,对象里面的函数的用法,用点语法。对象.方法()所有的函数都是某个对象的方法,所以js都是面向对象的语音
- 对象:类似图书馆,对图书进行分类管理,对象就是对函数(方法)进行管理。
- 什么叫封装:类似一个打火机,不需要知道打火机是如何制作的,会使就行。
- 函数也是一种变量:写函数时可以把他放在一个变量里面保存,可减少覆盖和被覆盖的风险。
可用对象收编变量(将所有的函数都作为一个对象的方法,这样就只有一个对象,)
var CheckObject = { checkName:function(){}, checkEmail:function(){}, checkPassword:function(){}, } //想要获取姓名:CHeckObject.checkName()
构造函数:新创建的对象都会有自己的一套方法(但这样做造成的消耗是很大的,可以通过prototype来写)
构造函数的标志:构造函数有this,有new,不能有return
var CheckObject = function(){
this.checkName = function(){}
this.checkEmail = function(){}
this.checkPassword = function(){}
}
var a = new function(){
a.checkName()
}
- 链式添加:
var methods = function(){};
methods.addMethods('checkName',function(){
return this
}).addMethods('checkEmail',function(){
return this
});
//测试一下:methods.checkName().checkEmail()
- 面向对象的编程:将你的需求抽象成一个对象,然后针对这个对象来分析其特性(属性)和动作(方法)这个对象我们称之为类。
面向对象
- 面向对象概念:一种编程思想。
- 核心:在解决任何问题的时候,首先试图去找到一个对象来帮助解决问题。
好处:1》身份:调度者; 2》代码的灵活度高; 3》可维护性高; 4》可扩展性高
坏处 : 1》可能造成代码的复杂度提高;2》可读性相对不好
- 核心:在解决任何问题的时候,首先试图去找到一个对象来帮助解决问题。
- 面向过程概念:身份:执行者
- 顺序:一般情况下不能打乱,从上之下一步一步执行。
- 面向对象的三大特性
1》 封装性: 将复杂的实现过程包装、隐藏起来,给用户提供使用接口。
2》继承性: 在js中,继承是体现在对象与对象之间的关系,继承就是指一个对象有权去访问别的对象上的成员。
3》多态性: 体现在子对象和父对象之间: 在父对象上的同一个行为,在各个子对象中的具体实现不同。 - JavaScript语言的特点:
1》 弱类型
2》多范式
3》基于对象的语言:在js中,一切都是对象。
4》基于原型的 创建对象的三种方式:
普通创建对象
var stu1 = new Object(); //小丽 等于 新 老婆(); stu1.name="小丽";//属性name就是一个变量 stu1.study =function(){ alert("我叫"+this.name+"爱学习"); }; //实现学习这个功能,行为 定义为函数 alert.name; alert.study(); 这种方式创建了多个学生对象,重复性代码很多
使用工厂模式创建对象:解决实例化对象产生大量重复的问题(属性和行为时一样的)
function createObj(name,age){ var obj = new Object(); obj.names =name; obj.ages = age; obj.study = function(){ alert("我叫"+this.names+"今年"+this.ages+"爱学习"); }; return obj;//返回的是对象的引用 } var stu3 =createObj("张三",23);//var stu3 = obj; //解释一下子: return obj 最后把obj这个对象返回了,返回的是个引用(var obj = new Object();),stu3接收到了obj这个引用,引用里存放的是new Object()这个对象的内存地址,所以stu3接收到的是这个对象的地址,stu3也就指向了张三这个对象 var stu4 =createObj("李四",22); 工厂模式存在的问题:不能确定对象具体的类型
使用构造函数(方法)创建特定的对象:
//根据创建对象来起名字 function Student (name,age){ this.name = name; //后台自动创建了 new Object() this.age = age; //this就指向了这个对象 this.study=function(){ alert(this.name+","+this.age); }; //后台返回了该对象 } var stu1 = new Student("zhang",23); alert(stu1.name); alert(stu1.age); stu1.study(); var stu2 = new Student("李斯",20); alert(stu2.name); alert(stu2.age); stu2.study(); //注意:构造函数和工厂模式的区别: 1、构造函数中直接给name、age赋值,没有显示的创建对象(new object()); 2、直接将属性和方法赋值给this对象; 3、没有使用return返回;
原型
- 原型概念:所谓原型就是一个函数的prototype属性所引用的对象。只要声明一个函数,那么原型就存在。
当通过该函数创建出来的每一个对象,都共享此原型,也就是说上述创建出来的所有对象可以直接访问到原型上的任何成员(属性和方法) - 原型的好处
实现同类对象的数据共享。 - 获取原型的方式
1》 通过函数:.prototype
2》通过对象:.proto - 对象的组成部分: 两部分: a: 对象本身;b: 他的原型
每一个对象都要_proto_属性。那么就是说,每一个对象都有原型 - 对象的类型:就是它构造函数的名字
- 原型属性:站在函数的角度,原型可以被称为该函数的原型属性
原型对象:站在对象的角度,原型可以被称为该对象的原型对象
_proto_兼容:非标准属性 - 原型的特性
1》动态性:
a: 给原型扩展成员会直接反应到已创建的对象身上
b: 替换原型对象,不会反映到已创建出来的对象。但是会直接影响之后创建出来的对象。
2》唯一性: 由同一函数创建出来的所有对象,共享同一个原型对象。
3》不可变性: 对象是无法改变原型对象上的任何成员
4》继承性: 所有对象都继承自它的原型对象 继承的实现方式:
1》 基于原型
a: 扩展原型 : 在原有的原型上进行相应的扩展,实现继承。
b: 置换原型 : 将要被继承的对象,直接替换掉原有的原型,实现继承
2》 拷贝继承: 将指定对象上的所有成员拷贝一份,并添加到当前对象上。这种继承方式成为拷贝继承。
3》 对象冒充: 在一个构造函数中,可以动态的添加一个parent方法,指向已有的构造函数,然后调用parent方法去实例化当前对象的一部分成员(或全部成员)。此时这种方式被称为对象冒充。注意:利用完parent属性之后,记得删除该属性。在实际开发中,两种继承方式可以组合起来应用。
extend方法的实现: 为了方便去实现拷贝继承。
- ES5 Object.create : 通过置换原型的方式来实现继承。
Object.create(parent):返回值为一个新的对象,并且该对象继承自parent对象。
var newObject = Object.create(parent);
原型链的概念
- javascript的结构:是一个对象,每一个对象都有一个私有属性[prototype]的原型,该prototype对象又有一个自己的原型,层层向上直到一个对象的原型诶null为止。(null就没有原型了,这就是原型链的最后一个环节。)
- 几个默认
a: 所有对象都有__proto__
属性,这个属性引用它的原型对象。
b: 原型对象继承自Object.prototype,具有constructor属性;如果置换了原型,记得要添加constructor属性。
c: 只有函数具有prototype属性(除了Function.prototype)。 - 概念:从当前对象到Object.prototype之间,存在一条层次分明,逐级递进的体现继承关系的链式结构。这个结构被称为原型链。
- 属性搜索原型: 当访问对象成员时,
1》 首先,在当前对象上查找,如果找到就直接返回(调用),停止查找;
2》如果没有找到,就向其原型对象上查找;如果找到就直接返回(调用),停止查找;
3》如果没有找到,就继续向原型对象的原型对象上查找,直到Object.prototype;
4》如果找到,那么就直接返回(调用),并停止查找;否则返回undefined(报错:xxx is not a function)
- 注意:
1》 如果访问对象的某个属性不存在的话,会搜索整个原型链。会导致js性能降低。
2》在实际开发中,尽量保持一个适度的原型链长度。
3》兼顾js性能以及代码的可读性可扩展性。
- 注意:
- Object.prototype介绍
1》 hasOwnProperty:判断当前对象的指定属性是否为自身的,而不是继承过来的;如果是本身的返回true;否则返回false。
2》isPrototypeOf:判断当前对象是否为指定对象的原型对象。如果是返回true;否则返回false。
如果当前对象出现在指定对象的原型链上就返回true;否则返回false。
3》propertyIsEnumerable:即判断指定属性是否为自身的,并且该属性也是可枚举的。
只有这两个条件都满足才返回true;否则返回false。 - 构造函数的执行过程
1 : 创建了一个空对象obj;
2:将obj赋值给this(让this指向obj)
3:将当前作用域交给this
4:执行构造函数内部的代码
5:将 this 返回。(return this;) - 绘制对象的原型链
绘制规则:
1: 绘制出简化的原型链
2 : 先将上述简化的原型链上的每一个对象绘制在图形上。
3:将每一个对象的默认属性添加上。
4:给每一个对象的属性添加指向。
5:如果每一个对象的属性都有了指向,说明原型链绘制完成。
继承
- 基于原型链的继承:
- 继承属性:JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
让我们假设我们有一个对象 o, 其有自己的属性 a 和 b:
{a: 1, b: 2}
o 的原型 o.__proto__有属性 b 和 c:
{b: 3, c: 4}
最后, o.__proto__.__proto__ 是 null.
这就是原型链的末尾,即 null,
根据定义,null 没有__proto__.
综上,整个原型链如下:
{a:1, b:2} ---> {b:3, c:4} ---> null
console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为1
console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为2
// o.__proto__上还有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)".
console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看o.__proto__上有没有.
// c是o.__proto__的自身属性吗?是的,该属性的值为4
console.log(o.d); // undefined
// d是o的自身属性吗?不是,那看看o.__proto__上有没有.
// d是o.__proto__的自身属性吗?不是,那看看o.__proto__.__proto__上有没有.
// o.__proto__.__proto__为null,停止搜索,
// 没有d属性,返回undefined
将属性设置为对象将创建自己的属性。获取和设置属性的唯一限制是内置 getter 或 setter 的属性。
- 继承方法:JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别,包括上面的“属性覆盖”(这种情况相当于其他语言的方法重写)。
- 当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。
var o = {
a: 2,
m: function(){
return this.a + 1;
}
};
console.log(o.m()); // 3
// 当调用 o.m 时,'this'指向了o.
var p = Object.create(o);
// p是一个对象, p.__proto__是o.
p.a = 4; // 创建 p 的自身属性a.
console.log(p.m()); // 5
// 调用 p.m 时, 'this'指向 p.
// 又因为 p 继承 o 的 m 函数
// 此时的'this.a' 即 p.a,即 p 的自身属性 'a'
- 使用不同的方法来创建对象和生成原型链
- 使用普通语法创建对象
var o = {a: 1};
// o这个对象继承了Object.prototype上面的所有属性
// 所以可以这样使用 o.hasOwnProperty('a').
// hasOwnProperty 是Object.prototype的自身属性。
// Object.prototype的原型为null。
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于Array.prototype
// (indexOf, forEach等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于Function.prototype
// (call, bind等方法都是从它继承而来):
// f ---> Function.prototype ---> Object.prototype ---> null
- 使用构造器创建对象
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.vertices = [];//在这里创建对象
this.edges = [];
}
Graph.prototype = {//在原型这里创建方法
addVertex: function(v){
this.vertices.push(v);
}
};
var g = new Graph();
// g是生成的对象,他的自身属性有'vertices'和'edges'.
// 在g被实例化时,g.__proto__指向了Graph.prototype.
- 使用 Object.create创建对象
ECMAScript 5 中引入了一个新方法:Object.create()。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:
var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
- 使用class关键字创建对象
ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。
"use strict";
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
class Square extends Polygon {
constructor(sideLength) {
super(sideLength, sideLength);
}
get area() {
return this.height * this.width;
}
set sideLength(newLength) {
this.height = newLength;
this.width = newLength;
}
}
var square = new Square(2);
- 性能:在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从Object.prototype继承的 hasOwnProperty 方法。