JS-高级-03

回顾

原型
 * 什么是原型?
 * 构造函数中有一个属性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();});

猜你喜欢

转载自blog.csdn.net/weixin_42966943/article/details/88603069