[TOC]

原型

面向对象

* 面向过程和面向对象都是编程的思想,方式不一样<br />    * 面向过程:凡事都是亲力亲为,所有的代码都要自己写,每一步都要很清楚,注重的是过程<br />    * 面向对象:执行者成为指挥者,只要找对象,然后让对象做相关的事情,注重的是结果<br />    * 面向对象的特性:封装,继承,多态<br />    * 封装:就是代码的封装,把一些特征和行为封装在对象中.<br />    * 面向对象的编程思想:根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,行为变成方法,然后定义(js)构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求.---编程的思想<br />    *<br />    * 对象:具体特指的某个事物,有特征(属性)和行为(方法)<br />    *

* 如何创建对象?

* 通过调用new Object(),还有{},自定义构造函数

*

* 创建对象的方式

* 1. 调用系统Object()----->创建出来的对象都是Object类型的,不能很明确的指出这个对象是属于什么类型<br />    * 2. 字面量的方式{}----->只能创建一个对象(一次只能创建一个)<br />    * 3.工厂模式创建对象----->----->推论---->自定义构造函数的方式<br />    *   自定义构造函数(优化后的工厂模式)<br />    *

* 自定义构造函数创建对象:4件事

*   1.在内存中申请一块空闲的空间,存储创建的对象<br />    *   2.this就是当前实例化的对象<br />    *   3.设置对象中的属性和方法(为对象添加属性和方法,为属性和方法赋值)<br />    *   4.把创建后的对象返回<br />    *   都是需要通过new的方式<br />    *<br />    *

* 什么是原型?

* **构造函数**中有一个属性prototype,是原型,程序员使用的<br />    *** 实例对象**中有一个属性__proto__,是原型,浏览器使用的,不是很标准的,<br />    * 实例对象中的__proto__指向的就是该实例对象中的构造函数中的prototype<br />    * 构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用<br />    * 正常的写法:实例对象.__proto__才能访问到构造函数中的prototype中的属性或者方法<br />    * per.__proto__.eat();//__proto__不是标准的属性<br />    * per.eat();//正确用法<br />    * 原型就是属性,而这个属性也是一个对象<br />    * Person.prototype--->是属性<br />    * Person.prototype.属性或者Person.ptototype.方法()

function Person(age) {
this.age=age;
this.study=function () {
}
}
Person.prototype.sex=”男”;//属性,属性在原型中
Person.prototype.sayHi=function () {//方法,方法在原型中
console.log(“您好”);
};
//实例化对象同时进行初始化
var per=new Person(10);
// console.log(per.proto.sex);
// per.proto.sayHi();
console.log(per.sex);
per.sayHi();

本身在构造函数中定义的属性和方法,当实例化对象的时候,实例对象中的属性和方法都是在自己的空间中存在的,如果是多个对象,这些属性和方法都会在单独的空间中存在,浪费内存空间,所以,为了数据共享,把想要节省空间的属性或者方法写在原型对象中,达到了数据共享,实现了节点内存空间
function Person(name){
this.name=name;
}
Person.prototype.sex=”男”;
var per=new Person(“小明”);
per.sex
var per2=new Person(“小芳”);
per2.sex
原型的作用之一:数据共享,节省内存空间

* 原型的写法:

* 构造函数.prototype.属性=值<br />    * 构造函数.prototype.方法=值---->函数.prototype,函数也是对象,所以,里面也有__proto__<br />    * 实例对象.prototype-------->实例对象中没有这个属性,只有__proto__(暂时的)<br />    *<br />    * 简单的原型的写法<br />    * 缺陷:--->新的知识点---->原型直接指向{}---->就是一个对象,没有构造器<br />    * 构造函数.prototype={<br />    * 切记:如果这这种写法,要把构造器加上<br />    * };<br /> Person.prototype={<br />     //简单的原型的写法,缺少构造器<br />     constructor:Person<br />   };<br />    *<br />    *<br />![原型及原型链.png](https://cdn.nlark.com/yuque/0/2019/png/297935/1557211979693-56a34346-d660-4737-9d7e-d86a9999847c.png#align=left&display=inline&height=531&originHeight=531&originWidth=760&size=24472&status=done&width=760)

* 通过原型为内置对象添加原型的属性或者方法

原因:

* 系统的内置对象的属性和方法可能不满足现在需求,所以,可以通过原型的方式加入属性或者方法,为了方便开发<br />    *<br />    * 为内置对象的原型中添加属性和方法,那么这个内置对象的实例对象就可以直接使用了<br />    * String.prototype.方法=匿名函数;<br />    * var str="哈哈";<br />    * str.方法();---->实例对象可以直接调用原型中的属性或者方法

* String.prototype.fdsfdsf=function(){};<br />    * 凡是string的实例对象都可以调用刚刚加入的方法

//为内置对象添加原型方法
String.prototype.sayHi=function () {
console.log(“字符串的打招呼的方法”);
};
//是一个实例对象
var str=”字符串”;
str.sayHi();
//实例对象调用属性或者方法,属性或者方法肯定是在构造函数中或者是构造函数的原型中

*案例:贪吃蛇

源代码:https://github.com/chen-xf/RetroSnakerGame
面向对象的思想来做: 分析对象,抽象出对象的特征和行为,定义构造函数,属性可以不共享
部分方法需要共享,方法加到prototype中定义(在原型中定义方法,数据共享,节省内存空间)

食物对象(食物的横纵坐标,宽和高,背景颜色)

食物需要画出来—-渲染出来—画,随机的画,在画食物的时候要先删除原来的食物


小蛇对象(宽,高,方向)
蛇需要画出来—-渲染出来—画,每走一次,需要把前一次的小蛇删除
蛇走的时候,需要方向,是否吃到了食物
小蛇移动的时候,是否吃了食物(吃了就要把小蛇的后面加一个食物的宽和高,颜色,无非就是把原来的蛇尾复制了一个加入到body中,———>把蛇尾拿出来再次加入到蛇尾的后面)

游戏对象(初始化食物,初始化小蛇,自动移动小蛇,判断按键)
自动的设置小蛇移动,判断小蛇是否撞墙,用户按下了什么方向键

window.变量=值;把这个局部变量的值暴露给window,成为了全局变量

* 对象.bind(参数);——>改变this的指向

原型链

使用对象——>使用对象中的属性和对象中的方法,使用对象就要先有构造函数
//构造函数
function Person(name,age) {
//属性
this.name=name;
this.age=age;
//在构造函数中的方法
this.eat=function () {
console.log(“吃好吃的”);
};
}
//添加共享的属性
Person.prototype.sex=”男”;
//添加共享的方法
Person.prototype.sayHi=function () {
console.log(“您好啊,怎么这么帅,就是这么帅”);
};
//实例化对象,并初始化
var per=new Person(“小明”,20);
per.sayHi();
如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也都是一样,那么,为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值
console.dir(per);//实例对象的结构
console.dir(Person);//构造函数的结构
实例对象的原型proto和构造函数的原型prototype指向是相同的
实例对象中的proto原型指向的是构造函数中的原型prototype
console.log(per.proto==Person.prototype);//true
实例对象中proto是原型,浏览器使用的
构造函数中的prototype是原型,程序员使用的
原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(proto)来联系的


divObj.proto——>HTMLDivElement.prototype的proto——>HTMLElement.prototype的proto
——>Element.prototype的proto——> Node.prototype的proto——>EventTarget.prototype的proto——>Object.prototype没有proto,所以,Object.prototype中的proto是null

原型链图解

原型链的图解.png

原型的指向是否可以改变

function Person(age){
this.age=age; console.log(this);}
Person.prototype.eat=function(){
console.log(“breakfast”);};
var per=new Person(10);console.log(per);
输出结果:
图片.png
构造函数中的this就是实例对象
原型对象中方法的this就是实例对象

function Person(age){
this.age=age;}
Person.prototype.eat=function(){
console.log(“breakfast”);};
function Student(){}
Student.prototype.study=function(){
console.log(“study”);};
Student.prototype=new Person(10);
var stu=new Student();
//stu.study();//原型的指向改变,调用该方法会报错
图片.png
stu.eat();//可以调用Person的eat()方法
图片.png
原型指向改变
原型链指向改变.png

原型指向后
原型指向后的图解.png
原型指向可以改变
实例对象的原型proto指向的是该对象所在的构造函数的原型对象
构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(proto)指向也会发生改变
实例对象和原型对象之间的关系是通过proto原型来联系起来的,这个关系就是原型链

原型链最终的指向是Object的prototype中的proto,是null
原型最终的指向.png

实例对象中有proto原型
构造函数中有prototype原型
prototype是对象
所以,prototype这个对象中也有proto,那么指向了哪里
实例对象中的proto指向的是构造函数的prototype
所以,prototype这个对象中proto指向的应该是某个构造函数的原型prototype
Person的prototype中的proto的指向
console.log(Person.prototype.proto);
per实例对象的proto———->Person.prototype的proto——>Object.prototype的proto是null

原型指向改变如何添加原型方法

//人的构造函数
function Person(age) {
this.age=age;
}
//人的原型中添加方法
Person.prototype.eat=function () {
console.log(“人正在吃东西”);
};
//学生构造函数
function Student(sex) {
this.sex=sex;
}

//改变了原型对象的指向
Student.prototype=new Person(10);
//学生的原型中添加方法——后在原型中添加方法
Student.prototype.sayHi=function () {
console.log(“您好哦”);
};
var stu=new Student(“男”);
stu.eat();
stu.sayHi();

console.dir(stu);
如果原型指向改变了,那么就应该在原型改变指向之后添加原型方法
function Person(age){
this.age=age;}

Person.prototype={
eat:function(){
console.log(“breakfast”); }
};
Person.prototype.sayHi=function(){
console.log(“hello”);};
var per=new Person(10);
per.eat();
per.sayHi();
输出结果:
图片.png

实例对象的属性和原型对象中的属性重名问题

实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就使用,找不到呢?=====
通过实例对象能否改变原型对象中的属性值?不能
per.sex=”人”;
就想改变原型对象中属性的值,怎么办?直接通过原型对象.属性=值;可以改变
Person.prototype.sex=”哦唛嘎的”;

继承

如何实现继承

原型的方式继承

借用构造函数继承

组合继承

拷贝继承

函数的不同的表现方式

函数的调用的不同的方式

this指向

严格模式

函数也是对象—-记住就可以了

对象不一定是函数

数组中的函数如何调用

apply和call