一、三种创建对象的方法
1.字面量
let obj1={
name:'zs',
age:20,
sex:'male',
eat:function () {
console.log(name+'在吃东西');
}
}
2.调用系统构造函数
let obj2=new Object();
obj2.name='ls';
obj2.age=18;
obj2.sleep=function(){
console.log('在睡觉');
}
3.自定义构造函数
1.构造函数中的this就是实例对象
2.原型对象的方法中的this也是实例对象
function Person(name,age) {
this.name=name;
this.age=age;
this.play=function () {
console.log('他在玩游戏')
}
}
//实例化一个对象,并对他的属性进行初始化
let woman=new Person('wang',55);
1.开辟空间存储对象;
2.把this设置为当前实例对象;
3.设置属性和方法的值;
4.把this对象返回。
4.工厂模式创建对象
通过创建函数的方式创建对象
function createObject(name,age) {
let obj=new Object();
obj.name='ls';
obj.age=18;
obj.sayHi=function(){
console.log('打招呼');
}
return obj;
}
let obj3=createObject('ww',4);
二、原型
__proto__:是浏览器使用的原型对象,在实例中存在
prototype:是程序员使用的原型对象,在构造函数中存在
1.判断实例对象是不是某个数据类型的对象的两种方法
dog是实例对象,判断dog是不是animal类型的,就判断它的构造器是不是指向animal
console.log(dog.__proto__.constructor==Animal);//方法一,也可以省略__proto__
console.log(dog instanceof Animal);//方法二,这种方式更准确,推荐使用,因为实例对象的原型也有可能指向Object
2.原型方法相互调用
3.原型的指向问题
1.构造函数和实例对象都指向原型对象;
2.原型对象中的构造器指向该原型对象的构造函数;
3.实例对象可以直接访问原型对象中的属性和方法;
4.构造函数中的prototype中的__proto__指向的是Objec中的prototype,prototype中有__proto__,最终指向null;
5.实例对象与原型对象间是通过__proto__来联系的,这个关系叫做原型链。
console.log('per.__proto__ == Person.prototype');//true
4.原型的指向可以改变
1.实例对象的__proto__指向的是该对象所在构造函数的原型对象;
2.构造函数的prototype的指向改变了,实例对象中__proto__的指向也会相应改变。
//人的构造函数
function Person(age) {
this.age=age
}
//人的原型方法
Person.prototype.sayHi=function () {
console.log('人打招呼');
};
//学生的构造函数
function Student() {}
//学生的构造方法
Student.prototype.eat=function () {
console.log('学生吃饭')
};
//学生的原型指向人的构造函数
Student.prototype=new Person(10);
//创建学生的实例对象
let stu=new Student();
stu.sayHi();//stu只能调用人的原型方法
//stu.eat();报错,因为原型的指向改变了
原型指向改变如何添加原型方法
在指向改变之后再添加原型方法
function Person(age) {
this.age=age
}
Person.prototype.sayHi=function () {
console.log('人打招呼');
};
function Student() { }
Student.prototype=new Person(10);//指向改变
Student.prototype.eat=function () {//再添加原型方法
console.log('学生吃饭')
};
let stu=new Student();
stu.sayHi();
stu.eat();//这里的eat就可以正常调用
5.原型的作用
数据共享,节省内存空间
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.sex='男';//原型中的属性
Person.prototype.play=function () {//原型中的方法
console.log('他在玩游戏')
};
let obj4=new Person('小红',5);
let obj5=new Person('小黑',8);
console.log(obj4.play == obj5.play);//true,指向同一个函数,节省了内容空间
实例对象中的属性与原型对象中的属性重名
1.先在实例函数中找,找不到在去prototype中找。
function Person(age,name) {
this.age=age
this.name=name;
}
Person.prototype.age=18;
let per = new Person(15,'张三');
console.log(per.age);//15
2.通过实例对象能否改变原型对象的属性?不能
per.age=14; //只能改变实例对象中的属性
function Person(age,name) {
this.age=age;
this.name=name;
}
per.prototype.age=20;//改变了原型对象的属性
console.log(per.age);//20
三、函数的自调用
一次性函数:函数声明的同时,进行函数调用
(function(win){
var num=0;//局部变量
//js是一门动态语言,对象没有这个属性,点了就有了
win.num=num;//把局部变量变成全局变量
})(window);
console.log(num);//报错,没有定义与赋值
四、面向对象的特征
1、封装
1.一个值存在一个变量中;
2.重复的代码放在一个函数中;
3.一系列属性放在一个对象中;
4.一些功能相似的函数(方法)放在一个对象中;
5.一些相似的对象放在一个js文件中。
2、多态
每个对象使用某个行为时会产生不同的结果。
想有多态就要先有继承,js中可以模拟多态,但不会去模拟,因为会导致数据不能共享。
3、继承
孩子继承父亲的属性和方法,同时又可以新增自己的属性和方法,继承是为了减少代码冗余
是一种关系,类与类之间的关系。
但js中没有类的概念,于是采用了构造函数来模拟类,通过原型实现继承。
①原型继承
通过改变原型的指向实现继承
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.eat=function () {
console.log("吃饭");
};
Student.prototype =new Person('小刘',18);
function Student(score) {
this.score=score;
}
Student.prototype.play=function () {
console.log("学生们在玩耍");
}
let stu =new Student(100);
//继承过来的
console.log(stu.name);
console.log(stu.age);
stu.eat();
//学生自己的
console.log(stu.score);
stu.play();
原型的作用一:数据共享节省内存空间
原型的作用二:实现继承
出现问题:原型实现的继承,如果使用多个实例,则他们的名字和年龄是相同的
②借用构造函数继承
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.eat=function () {
console.log("吃饭");
};
function Student(name,age,score) {
Person.call(this,name,age,);
this.score=score;
}
Student.prototype.play=function () {
console.log("学生们在玩耍");
};
let stu1 =new Student("小刘",18,100);
console.log(stu1.name,stu1.age,stu1.score);
let stu2 =new Student("小魏",3,101);
console.log(stu2.name,stu2.age,stu2.score);
出现问题:借用构造函数实现的继承,不能继承父亲的方法
③组合继承
组合继承通过结合原型继承和借用构造函数继承的方式,解决了两者的不足
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.eat=function () {
console.log("吃饭");
};
function Student(name,age,score) {
Person.call(this,name,age);//借用构造函数继承,只继承属性
this.score=score;
}
Student.prototype=new Person();//原型继承,因没有传值,这里就只继承方法
Student.prototype.play=function () {
console.log("学生们在玩耍");
};
let stu1 =new Student("小刘",18,100);
console.log(stu1.name,stu1.age,stu1.score);
let stu2 =new Student("小魏",3,101);
console.log(stu2.name,stu2.age,stu2.score);
stu1.play();
stu1.eat();
④拷贝继承
直接把某个对象赋值给另一个对象,也是拷贝赋值 (let obj1=obj2;),
但这样仅仅是改变了地址的指向,在堆里没有自己的空间。
function Person() {}
Person.prototype.name='小刘';
Person.prototype.age=18;
Person.prototype.eat=function () {
console.log("吃饭");
};
let obj={};
for (let key in Person.prototype){
obj[key]=Person.prototype[key];
}
console.log(obj.name,obj.age);
obj.eat();
五、闭包
闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<div>a</div>
<div>b</div>
<div>c</div>
<div>d</div>
<script type="text/javascript">
var divs=document.getElementsByTagName("div");
for (var i=0;i<divs.length;i++) {
divs[i].onclick=function(){
alert(i);
}
}
</script>
</body>
</html>
六、this的指向
1.普通函数中的this
window:BOM 中顶级对象是 window,浏览器中所有定西都是 window 的
2.对象.方法中的this
实例对象
function Student() {
console.log(this);
this.sayHi=function () {
console.log(this);//实例对象
}
}
let stu = new Student();
console.log(stu);
3.定时器中的this
window:window 调用的方法可以省略 window
window.setInterval(function () {
console.log(this)
},1000);
4.构造函数中的this
实例对象
function Student() {
console.log(this);
}
let stu = new Student();
console.log(stu);
5.原型对象方法中的this
实例对象
function Student() {
console.log(this);
}
Person.prototype.sleep=function(){
console.log(this);
};
let stu = new Student();
console.log(stu);
七、严格模式
必须通过对象调用方法
"use strict";
function f1() {
console.log(this);//window
}
window.f1();