回顾
原型
* 什么是原型?
* 构造函数中有一个属性prototype,是原型,程序员使用的
* 实例对象中有一个属性__proto__,是原型,浏览器使用的,不是很标准的,
* 实例对象中的__proto__指向的就是该实例对象中的构造函数中的prototype
* 构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用
* 正常的写法:实例对象.__proto__才能访问到构造函数中的prototype中的属性或者方法
* per.__proto__.eat();//__proto__不是标准的属性
* per.eat();
* 原型就是属性,而这个属性也是一个对象
* Person.prototype--->是属性
* Person.prototype.属性或者Person.ptototype.方法()
* 本身在构造函数中定义的属性和方法,
当实例化对象的时候,实例对象中的属性和方法都是在自己的空间中存在的;
如果是多个对象,这些属性和方法都会在单独的空间中存在,浪费内存空间;
所以,为了数据共享,把想要节省空间的属性或者方法写在原型对象中,
达到了数据共享,实现了节点内存空间
function Person(name){
this.name=name;
}
Person.prototype.sex="男";
var per=new Person("小明");
per.sex
var per2=new Person("小芳");
per2.sex
原型的作用之一:数据共享,节省内存空间
* 原型的写法:
* 构造函数.prototype.属性=值
* 构造函数.prototype.方法=值---->函数.prototype,函数也是对象,所以,里面也有__proto__
* 实例对象.prototype-------->实例对象中没有这个属性,只有__proto__(暂时的)
* 简单的原型的写法
* 缺陷:--->新的知识点---->原型直接指向{}---->就是一个对象,没有构造器
构造函数.prototype={
切记:如果这这种写法,要把构造器加上
};
* 通过原型为内置对象(String、Date、Array)添加原型的属性或者方法----->原因:
* 系统的内置对象的属性和方法可能不满足现在需求,所以,可以通过原型的方式加入属性或者方法,为了方便开发
* 为内置对象的原型中添加属性和方法,那么这个内置对象的实例对象就可以直接使用了
* String.prototype.方法=匿名函数;
* var str="哈哈";
* str.方法();---->实例对象可以直接调用原型中的属性或者方法
* String.prototype.fdsfdsf=function(){};
* 凡是string的实例对象都可以调用刚刚加入的方法
面向对象的思想处理项目
* 面向对象的思想来做: 分析对象,抽象出对象的特征和行为,定义构造函数,属性可以不共享
* 部分方法需要共享,方法加到prototype中定义(在原型中定义方法,数据共享,节省内存空间)
贪吃蛇
* 食物对象(食物的横纵坐标,宽和高,背景颜色)
* 食物需要画出来---渲染出来--画,随机的画,在画食物的时候要先删除原来的食物
* 小蛇对象(宽,高,方向)
* 蛇需要画出来---渲染出来--画,每走一次,需要把前一次的小蛇删除
* 蛇走的时候,需要方向,是否吃到了食物
* 小蛇移动的时候,是否吃了食物(吃了就要把小蛇的后面加一个食物的宽和高,颜色,无非就是把原来的蛇尾复制了一个加入到body中,------>把蛇尾拿出来再次加入到蛇尾的后面)
* 游戏对象(初始化食物,初始化小蛇,自动移动小蛇,判断按键)
* 自动的设置小蛇移动,判断小蛇是否撞墙,用户按下了什么方向键
* window.变量=值;把这个局部变量的值暴露给window,成为了全局变量
* 对象.bind(参数);---->改变this的指向
原型加深
原型作用:
1.共享数据,节省内存空间
2.实现继承
实例对象的原型__proto__和构造函数的原型prototype指向是相同的
构造函数中的this就是实例对象
原型对象中的this也是实例对象
原型指向可以改变—示意图如下
原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的
总结:构造函数的原型对象(prototype)指向如果改变,实例对象的原型(__proto__)的指向也会发生改变

原型的最终指向
推理:
1.实例对象有__proto__原型
2.构造函数有prototype原型
3.prototype是对象
4.所以,prototype这个对象也有__proto__==>它指向哪??
5.实例对象中的__proto__指向构造函数的prototype
6.所以,prototype对象中的__proto__指向应是某构造函数的prototype
function Person() {}
Person.prototype.eat=function () {
console.log("吃东西");
};
var per=new Person();
console.log(per.__proto__==Person.prototype); //true
console.log(per.__proto__.__proto__==Person.prototype.__proto__); //true
console.log(Person.prototype.__proto__==Object.prototype); //true
console.log(Object.prototype.__proto__);
总结:原型的最终指向---系统构造函数Object的原型prototype的__proto__-->null

原型指向改变–添加原型属性和方法—访问
如果原型指向改变了
那么需要在原型指向改变之后再添加原型方法,否则无法访问到
实例对象的属性与原型对象的属性重名
实例对象属性优先级高于原型对象的属性
实例对象访问属性,先从实例对象中找,找不到就去指向的原型对象中找;如果都找不到===》undefined
原因:因为JS是一门动态类型的语言,对象没有什么,只要点了,那么这个对象就有了这个东西;
只要对象.属性名字,对象就有这个属性了,但是,该属性没有赋值,所以,结是:undefined
通过实例对象能否改变原型对象中的属性值?不能
就想改变原型对象中属性的值,怎么办?直接通过原型对象.属性=值;可以改变
页面元素对象的原型链
原型链:实例对象和原型对象之间的关系,通过__proto__来联系
<div id="dv"></div>
<script>
var divObj=document.getElementById("dv");
console.dir(divObj);
</script>
1.divObj.__proto__---->HTMLDivElement.prototype
2.HTMLDivElement.prototype的__proto__--->HTMLElement.prototype的__proto__
3.HTMLElement.prototype的__proto__---->Element.prototype的__proto__
4.Element.prototype的__proto__---->Node.prototype的__proto__
5.Node.prototype的__proto__---->EventTarget.prototype的__proto__
6.EventTarget.prototype的__proto__---->Object.prototype
7.Object.prototype没有__proto__,所以,Object.prototype中的__proto__是null
继承
面向对象的编程思想:根据需求,分析对象,找到对象的特征和行为,通过代码的方式实现需求
创建构造函数--》创建对象--》调用相应的属性及方法实现相应的功能及需求。
JS不是面向对象语言,是基于对象的语言,因为面向对象的思想适合人的思想 ,编程更方便,代码易维护--->学习面向对象编程
面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言;
所以,JS中没有类(class),但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class)
面向对象的特性:
封装:将重复的代码包装起来,需要时调用-(代码重用)
继承:是一种关系,类与类的关系,JS中通过构造函数模拟类,然后通过原型来实现继承
多态:一个对象有不同的行为,或同一个行为针对不同的对象,产生不同的结果
要想有多态,需先有继承,JS中可以实现继承

js继承
1.改变原型的指向到欲继承的构造函数--->实现继承
对象名.prototype = new 欲继承对象();
缺陷:因为改变原型的指向的同时实现了继承,直接初始化了属性,继承过来的属性都是一样的了
只能重新调用对象的属性进行重新赋值
解决方案:继承的时候,不用改变原型的指向,通过直接调用父级的构造函数的方式来为属性赋值----->借用构造函数:调用欲继承的对象的构造函数使用
2.借用构造函数实现继承
//借用构造函数:构造函数名字.call(当前对象属性,继承对象属性);
作用:解决了属性继承,并且值不重复的问题
缺陷:父级类别中的方法不能继承
function Person(name, age, sex, weight) {
this.name = name;
this.age = age;
this.sex = sex;
this.weight = weight;
}
Person.prototype.sayHi = function () {
console.log("您好");
};
function Student(name,age,sex,weight,score) {
//借用构造函数 !!!!!!!!!!!!!!!!!
Person.call(this,name,age,sex,weight);
this.score = score;
}
var stu1 = new Student("小明",10,"男","10kg","100");
console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
var stu2 = new Student("小红",20,"女","20kg","120");
console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);
3.组合继承
将原型继承和借用构造函数继承组合到一起
////可以完全继承对象的属性及方法!!!!!
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function () {
console.log("阿涅哈斯诶呦");
};
function Student(name,age,sex,score) {
//借用构造函数:属性值重复的问题
Person.call(this,name,age,sex); !!!!!!!!!!!
this.score=score;
}
//改变原型指向----继承 !!!!!!!!!!!!!
Student.prototype=new Person();//不传值
Student.prototype.eat=function () {
console.log("吃东西");
};
var stu=new Student("小黑",20,"男","100分");
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi();
stu.eat();
var stu2=new Student("小黑黑",200,"男人","1010分");
console.log(stu2.name,stu2.age,stu2.sex,stu2.score);
stu2.sayHi();
stu2.eat();
//成功继承了Person得属性及方法!!!!!
4.浅拷贝继承
将一个对象中的属性和方法直接复制到另一个对象中
var obj1={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
//仅改变了地址的指向,并没有复制
var obj2=obj1;
console.log(obj2.name,obj2.age);
obj2.sleep();
----------------------------------------------------------------------------------------------
function Person() {}
Person.prototype.age=10;
Person.prototype.sex="男";
Person.prototype.height=100;
Person.prototype.play=function () {
console.log("玩的好开心");
};
var obj2={};
//Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法
//复制
for(var key in Person.prototype){
obj2[key]=Person.prototype[key];
}
console.dir(obj2);
obj2.play();
总结继承
继承:类与类之间的关系--->(面向对象语言)为多态服务
js模拟(原型)面向对象、继承----->节省内存空间
原型
作用:数据共享、继承 目的:节省内存空间
继承的几种方式
1.原型继承:改变原型指向----->属性固定、不好更改
2.借用构造函数继承:------>属性继承,但方法不能继承
3.组合继承:原型继承+借用构造函数继承 //既能解决属性问题,又能解决方法问题
4.浅拷贝继承:把对象中需要共享的属性和方法,直接通过遍历的方式复制到另一个对象中
逆推对象看原型

函数
函数也是对象----所有函数实际上都是通过Function的构造函数创建出来的实例对象
但对象不一定是函数
var f1=new Function("num1","num2","return num1+num2");
等价于:------------------------------------------------------------------------------
function f1(num1,num2){
return num1+num2
}
console.log(f1(10,20)); ===》30
console.log(f1.__proto__==Function.prototype); ==>true
所以,函数实际上也是对象
函数角色:声明、表示
普通函数中的this----------->window
对象.方法中的this---------->当前的实例对象
定时器方法中的this-------->window
构造函数中的this----------->实例对象
原型对象方法中的this----->实例对象
严格模式:
"use strict";
数组中的函数调用
var array = [
function(){},
function(){},
function(){}
];
//回调函数--函数作为参数的函数
array.forEach(function(ele){ele();});