javaScript面向对象

一,面向对象编程

1.在面向对象编程有几个基本的概念:

  • Class :定义对象抽象特点,包含它的属性和方法
  • 对象 Object :类的实例,通过new生成
  • 封装:对数据进行封装
  • 继承:子类继承父类
  • 多态:由继承而产生了相关的不同的类,对同一个方法可以有不同的响应
  • 存取器:用以改变属性的读取和赋值行为
  • 修饰符:修饰符是一些关键字,用于限定成员或类型的性质
  • 抽象类
  • 接口

2.然而直到ES5没有都没有类Class的概念,且在ES5中是通过原型prototype来实现面向对象编程

二,原型和原型链

1.js中,每个实例对象都内置了一个__proto__属性,每个函数都内置了一个prototype属性,指向原型对象,且原型本身就是一个对象。

2.当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 _ proto _ 里找这个属性,这个 _ proto _ 又会有自己的 _ proto _,于是就这样一直找下去,直到null,也就是我们平时所说的原型链的概念。对于一个数组,其原型和形成的原型链如下图所示:
在这里插入图片描述

3.关键字 instanceof 用于判断构造函数的prototype 是否出现在实例对象的原型链上。

function C() {}
let obj = new C();
obj instanceof C; //true
obj instanceof Object; //true

三,面向对象编程方法

1.在es5中没有class的概念,因此面向对象是使用构造函数来实现

2.为了区分普通函数和构造函数,通常构造函数首字母大写。

function Animal(sound) {
  this.sound = sound;
}

3.构造函数通常没有返回值,或者设置成返回当前实例对象return this。如果我们给js的构造函数设置返回值需要注意:

(1)当return一个基本数据类型,new一个实例返回实例对象,return等于无效。

(2)而return一个引用数据类型时,new一个实例返回的就是该引用类型。

function Animal(sound) {
  this.sound = sound;
  return 10;
}
new Animal("hh"); //{sound:'hh',__proto__:Animal.prototype}
function Animal(sound) {
  this.sound = sound;
  return { K: 1 };
}
new Animal("hh"); // {K:1}

4.可以看出,构造函数就只是一个普通函数,因此他也可以作为普通函数使用,总之,在es6之前,类都是很不强的概念,

5.我们使用new来创建一个实例对象,此时函数内部的this指向当前对象。如何作为普通函数使用那么内部的this就要视情况而定了。

6.当我们new一个实例对象时具体做了什么?对于let a = new A()
(1)首先创建了一个空对象:let a={}
(2)让a的原型指向构造函数的原型:a.__proto__=A.prototype
(3)调用构造函数,使构造函数的this指向这个对象:A.call(a)
这样就创建好了一个实例对象。根据这个过程我们可以自己实现一个new

Person.prototype.introduce = function () {
  console.log(`my name is ${this.name}`);
};

function myNew(fn, ...rest) {
  let obj = {}; //创建空对象
  obj.__proto__ = fn.prototype; //改变对象原型
  fn.apply(obj, rest); // 调用构造函数,使构造函数内部this指向obj
  return obj;
}

let myMax = myNew(Person, "max", "female");
let max = new Person("max", "female");

myMax.introduce();

console.log("myNew", myMax);
console.log("new", max);

运行结果:可以发现两个对象完全相同。
在这里插入图片描述

5.对于一个构造函数,其原型链如下图所示:
在这里插入图片描述

四,在ES5中完整的一个基本类

1.注意,实例属性不在原型上,实例方法定义在原型上

function Animal(type) {
  this.type = type; //实例属性
}
Animal.prototype.kindOf = function () {
  console.log("i am " + this.type);
}; //实例方法

Animal.intelligence = "below human"; //静态属性
Animal.say = function () {
  console.log("not human lang");
}; //静态方法

五,js面向对象继承的实现。

1.原型链继承
无法向父类传参
父类的引用实例属性被所有实例共享

function Person(name,sex)
{
	this.obj={type:"引用属性值"}
	this.name=name;
	this.sex=sex;
}
Person.prototype.introduce=function()
{
	console.log(`my name is ${this.name}`);
}

American.prototype=new Person()

function American(skin)
{
	this.skin=skin
}

let p = new American("white") //创建一个实例无法向父类传参
let p1 = new American("white")
p.obj.type="在p中修改obj"
console.log(p1.obj) // {type: "在p中修改obj"} // 父类的引用属性被子类实例共享。

2.借用构造函数
能够为父类传递参数,且继承了父类构造函数里的实例属性,和实例方法。

缺点:
无法使用父类在原型上定义的一切,包括方法,(因为父类的原型并没有在该创建对象的原型链上)

3.组合式继承
使用call和apply在子类调用父类的构造函数传参,使用原型链来实现复用方法
父类被调用了两次

4.寄生组合式继承
寄生组合式继承是最好的继承,解决了组合式继承调用两次父构造函数的问题。

function object(obj) {
  function F() {}
  F.prototype = obj.prototype;
  return new F();
}
function prototypeInherit(SuperClass, Sub) {
  prototype = object(superClass);
  prototype.constructor = sub;
  Sub.prototype = prototype;
}

function Sub(name, age) {
  SuperClass.call(this, name); //重要的一步,相当于es6 class 的 super
  this.age = age;
}

注意,子类原型方法的定义只能在继承父类之后执行,否则跟没写一样,会在继承的过程中被覆盖。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值