一、基础知识复习
实例对象的_proto_和构造函数的prototype的指向是相同的。
实例对象中的_proto_是原形,浏览器使用的
构造函数中的prototype是原形是给程序员使用的
下面介绍的原型链是一种关系,是实例对象和原形对象之间的关系。关系是通过_proto_来联系的。
二、原形的指向_proto_最终指向了哪里?
实例对象中有__proto__原型
构造函数中有prototype原型
prototype是对象
所以,prototype这个对象中也有__proto__,那么指向了哪里
实例对象中的__proto__指向的是构造函数的prototype
所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype,最终指向的是object.prototype 是null
function Person() {
}
Person.prototype.eat=function () {
console.log("吃东西");
};
var per=new Person();
console.dir(per);
console.dir(Person);
console.log(per.__proto__==Person.prototype);
console.log(per.__proto__.__proto__==Person.prototype.__proto__);console.log(Person.prototype.__proto__==Object.prototype);
console.log(Object.prototype.__proto__);
三、实例对象和原形对象属性重名的问题
JS是一种动态类型的语言,对象要有什么直接点语法添加就可以。如果没有这个属性,直接 对象.属性名字就有了。如果没有赋值是undefined;
如果想改变原形对象中的属性,需要的格式是: 原形对象.属性=值; Person.prototype.sex="哦唛嘎的" per.sex="人";//这样只能够改变自己的属性值,不能改变原形中的属性值。
function Person(age,sex) {
this.age=age;
this.sex=sex;
}
Person.prototype.sex="女";
var per=new Person(10,"男");
console.log(per.sex);
//因为JS是一门动态类型的语言,对象没有什么,只要点了,那么这个对象就有了这个东西,没有这个属性,只要对象.属性名字,对象就有这个属性了,但是,该属性没有赋值,所以,结果是:undefined
console.log(per.fdsfdsfsdfds);//是undefine 因为JS是一种动态类型语言,只要用点语法就有了。如果没有赋值是undefined
console.log(fsdfdsfds);//由于当成一个变量输出,报错。
Person.prototype.sex="哦唛嘎的";
四、继承
1.原型链:是实例对象和原形对象之间的关系,通过_proto_来联系。
<script>
var divObj=document.getElementById("dv");
console.dir(divObj);
//divObj.__proto__---->HTMLDivElement.prototype的__proto__--->HTMLElement.prototype的__proto__---->Element.prototype的__proto__---->Node.prototype的__proto__---->EventTarget.prototype的__proto__---->Object.prototype没有__proto__,所以,Object.prototype中的__proto__是null
</script>
2.继承前奏
面向对象编程思想:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,要想实现这个需求,就要创建对象,要想创建对象,就应该显示有构造函数,然后通过构造函数来创建对象.,通过对象调用属性和方法来实现相应的功能及需求,即可
首先JS不是一门面向对象的语言,JS是一门基于对象的语言,那么为什么学习js还要学习面向对象,因为面向对象的思想适合于人的想法,编程起来会更加的方便,及后期的维护....
面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言,所以,JS中没有类(class),但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class)
面向对象的特新:继承、封装、多态。
* 封装:就是包装
* 一个值存储在一个变量中--封装
* 一坨重复代码放在一个函数中--封装
* 一系列的属性放在一个对象中--封装
* 一些功能类似的函数(方法)放在一个对象中--封装
* 好多相类似的对象放在一个js文件中---封装
*
* 继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承
* 继承也是为了数据共享,js中的继承也是为了实现数据共享
*
* 原型作用之一:数据共享,节省内存空间
* 原型作用之二:为了实现继承
*
* 继承是一种关系:
*
* 父类级别与类级别的关系
*
* 例子:
*
* 小杨--->人, 姓名, 有钱, 帅, 有功夫--降龙十八掌
* 小杨子-->人,
* 继承:
* 姓氏----继承
* 外表----继承
* 财产----继承
* 功夫---继承
*
*
* 人: 姓名, 性别, 年龄 ,吃饭, 睡觉
*
* 学生类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 学习行为
* 老师类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资,教学行为
* 程序员: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资, 敲代码
* 司机类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资 开车
*
*
* 动物类别: 体重, 颜色, 吃
* 狗类别: 体重,颜色, 吃, 咬人
* 二哈类别: 体重,颜色, 吃, 咬人 逗主人开心,汪汪,你好帅
*
*
*
*
*
*
* 多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟,
//例子:
//人,都有姓名,性别,年龄, 吃饭, 睡觉, 玩
//学生,都有姓名,性别,年龄, 成绩, 吃饭, 睡觉, 玩 ,学习的行为
//js中通过原型来实现继承
this.name=name;
this.sex=sex;
this.age=age;
}
Person.prototype.eat=function () {
console.log("人可以吃东西");
};
Person.prototype.sleep=function () {
console.log("人在睡觉");
};
Person.prototype.play=function () {
console.log("生活就是不一样的玩法而已");
};
function Student(score) {
this.score=score;
}
//改变学生的原型的指向即可==========>学生和人已经发生关系
Student.prototype=new Person("小明",10,"男");
Student.prototype.study=function () {
console.log("学习很累很累的哦.");
};
//相同的代码太多,造成了代码的冗余(重复的代码)
var stu=new Student(100);
console.log(stu.name);
console.log(stu.age);
console.log(stu.sex);
stu.eat();
stu.play();
stu.sleep();
console.log("下面的是学生对象中自己有的");
console.log(stu.score);
stu.study();
3.继承的两种方式,一种是改变原形的指向。一种是直接调用父级的构造函数。
改变原形指向:
function Person (name,age,sex,height) {
this.name=name;
this.age=age;
this.sex=sex;
this.height=height;
}
Person.prototype.sayHai=function(){
console.log("您好");
}
function Student(score){
this.score=score;
}
Student.prototype=new Person("jack",20,"男","50kg");
Student.prototype.eat=function(){
console.log("我特别能吃");
}
var student=new Student();
console.log(student.name,student.age);
借用构造函数:构造含糊名字.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);
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);
4.拷贝继承
就是把一个对象中的属性和方法,复制到另一个对象中。
一种是改变地址指向的继承,这种继承只是在栈中创建一个变量指obj2指向内存中的obj1。并没有开辟新的内存空间。var obj2=obj1;
一种是创建一个新的对象,这种继承是在内存中重新开辟空间,然后两个对象引用的指针的指向相同。
var obj1={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
var obj2={};
for(var key in obj1){
obj2[key]=obj1[key];
}
console.log(obj2.name);
5、总结继承
继承:
* 原型作用: 数据共享 ,目的是:为了节省内存空间,
* 原型作用: 继承 目的是:为了节省内存空间
*
* 原型继承:改变原型的指向
* 借用构造函数继承:主要解决属性的问题
* 组合继承:原型继承+借用构造函数继承
* 既能解决属性问题,又能解决方法问题
* 拷贝继承:就是把对象中需要共享的属性或者犯法,直接遍历的方式复制到另一个对象中
五、函数的角色
1.函数声明和函数表达式
<script>
//函数的角色:
//函数的声明
function f1() {
console.log("我是函数");
}
f1();
//函数表达式
var ff=function () {
console.log("我也是一个函数");
};
ff();
</script>
2.函数中this指向
BOM中的顶级对象是window,浏览器中所有的东西都是window的。
* 普通函数中的this是谁?-----window
* 对象.方法中的this是谁?----当前的实例对象
* 定时器方法中的this是谁?----window
* 构造函数中的this是谁?-----实例对象
* 原型对象方法中的this是谁?---实例对象
//普通函数
function f1() {
console.log(this);
}
f1();
//定时器中的this
setInterval(function () {
console.log(this);
},1000);
//构造函数
function Person() {
console.log(this);
//对象的方法
this.sayHi=function () {
console.log(this);
};
}
//原型中的方法
Person.prototype.eat=function () {
console.log(this);
};
var per=new Person();
console.log(per);
per.sayHi();
per.eat();
//BOM:中顶级对象是window,浏览器中所有的东西都是window的
3.函数中的不同的调用方式
普通函数的调用方式
构造函数的调用方式
//普通函数
function f1() {
console.log("文能提笔控萝莉");
}
f1();
//构造函数---通过new 来调用,创建对象
function F1() {
console.log("我是构造函数,我骄傲");
}
var f=new F1();
//对象的方法
function Person() {
this.play=function () {
console.log("玩代码");
};
}
var per=new Person();
per.play();
六、函数和对象的关系
所有的函数的源头是Function(){ } 所有的函数都可以看成是Function创建出来的实例对象
函数是对象,对象不一定是函数。推理如下:
由于对象都存在_proto_原形,
函数中都存在prototype原形,
这两个原形都是什么?对象 Object类型
所以:函数中的prototype中存在_proto_原形
function F1() {
}
console.dir(F1);
console.dir(Math);//中有__proto__,但是没有prorotype
//对象中有__proto__,函数中应该有prototype
总结:如果一个东西里面有prototype,又有__proto__,说明是函数,也是对象
//所有的函数实际上都是Function的构造函数创建出来的实例对象
var f1=new Function("num1","num2","return num1+num2");console.log(f1(10,20));
console.log(f1.__proto__==Function.prototype);
// 所以,函数实际上也是对象
console.dir(f1);
console.dir(Function);