JavaScript学习笔记 08、面向对象(上)
@[toc]
前言
本篇博客是关于javascript中面向对象知识点,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
一、认识对象
1.1、对象的定义
语法:
//注意点1:js中使用{}来表示对象,每组k:v之间使用逗号相隔
//注意点2:其中k一般不用引号,对于不符合命名规范的时候''包裹;v可以使用任意类型(基本类型,引用类型)
var obj = {
k: v,
k: v,
k: v,
k: v //注意点3:最后一个键值对可以不写逗号
}
示例:
<script>
var changlu = {
name: "cl",
age: 18,
sex: '男',
hobbys: ['编程','java','运动'],
'favourite-boot': '刻意练习' //属性名(key)中若是有不符合命名规范的要用''包裹
};
console.log(changlu);
</script>
1.2、访问对象值(两种形式)
两种形式:
- 使用
obj.属性
方式直接访问原本在对象中key没有’'包裹的值。 - 使用
obj['属性名']
来访问key有’’(或无’'也是可以的)包裹的值。
我们就拿上面的示例进行举例:
<script>
var changlu = {
name: "cl",
age: 18,
sex: '男',
hobbys: ['编程', 'java', '运动'],
'favourite-boot': '刻意练习' //属性名(key)中若是有不符合命名规范的要用''包裹
};
console.log(changlu);
//方式一:直接使用.属性方式
console.log(changlu.name);
console.log(changlu.age);
console.log(changlu.sex);
console.log(changlu.hobbys);
//方式二:使用[]的形式来访问值
//针对于获取键使用''包裹的值。
console.log(changlu['favourite-boot']);
//访问正常的key值使用这类方式也是ok的
//console.log(changlu['name']);
//访问存储到变量名key对应的值
var searchName = "age";
console.log(changlu[searchName]);
</script>
小总结:对于正确命名规范的key
,我们可以直接使用.属性的方式进行访问;使用['属性名']
是能够访问到指定对象中的所有属性的!
1.3、修改、创建、删除对象值
介绍
修改:直接访问对象值进行修改即可。
创建:直接.属性即可创建。
删除:delete obj.属性
或delete obj['属性']
示例
<script>
var changlu = {
name: "cl",
age: 18,
sex: '男',
hobbys: ['编程', 'java', '运动'],
'favourite-boot': '刻意练习' //属性名(key)中若是有不符合命名规范的要用''包裹
};
//修改
changlu.age = 20;//直接修改即可
console.log(changlu.age);
//创建一个身高height
changlu.height = 180;//直接赋值就好
console.log(changlu.height);
//删除身高height以及key被''包裹的键值对
delete changlu.height;
delete changlu['favourite-boot'];
console.log(changlu);
</script>
1.4、方法的创建与使用
对于对象方法的创建你可以直接声明在在内部,或者在外部进行调用对象.新函数= function(){}
;
示例:
<body>
<script>
var changlu = {
name: "cl",
age: 18,
sex: '男',
info: function () {//创建函数方式(方式一)
//在对象的函数中你不能直接调用该对象的属性,只能使用this.属性或者对象名.属性获取。
console.log("My name is " + this.name + "," + "age:" + changlu.age);
}
};
//调用内部函数
changlu.info();
//创建函数方式(方式二)
changlu.sleep = function () {
console.log("I will be sleep!");
}
//调用函数
changlu.sleep();
console.log(changlu);
</script>
</body>
1.5、遍历对象(for…in…)
语法:for(var key in 对象)
,该key是对象的键,其是一个字符串。
示例:
<script>
var changlu = {
name: "cl",
age: 18,
sex: '男',
info: function () {//创建函数方式(方式一)
//在对象的函数中你不能直接调用该对象的属性,只能使用this.属性或者对象名.属性获取。
console.log("My name is " + this.name + "," + "age:" + changlu.age);
}
};
//遍历对象
for (const key in changlu) {
console.log(typeof key);//该key变量为string类型
console.log("key:" + key + ",value:" + changlu[key])
}
</script>
1.6、对象的深浅克隆(针对于object)
浅克隆
使用for ... in ...
来实现对象的浅克隆:对于对象中的引用类型实际上仅仅只是引用赋值
<script>
var obj = {
name: "cl",
age: 18,
t: [0, 1, 2, 3, {
a: 1,
b: 2,
c: [11, 22, 33]
}]
};
//浅克隆:for...in...
var newObj = {};
for (var key in obj) {
newObj[key] = obj[key];
}
//测试
console.log(newObj);
//证明是浅克隆:第三个属性是引用类型t,若是两个值相同则表示是没有成功
console.log(newObj.t == obj.t);
</script>
深克隆
对于深克隆需要通过使用递归的形式进行:
<script>
var obj = {
name: "cl",
age: 18,
t: [0, 1, 2, 3, {
a: 1,
b: 2,
c: [11, 22, 33]
}]
};
//深克隆(数组、对象、其他类型)
function deepClone(o) {
//判断是否是数组(数组类型实际就是object,所有需要提前进行判断)
if (Array.isArray(o)) {
var result = [];
//数组
for (let i = 0; i < o.length; i++) {
result.push(deepClone(o[i]));//进行递归调用
}
} else if (typeof o == "object") {
//需要克隆的是对象
var result = {};
for (let key in o) {
result[key] = deepClone(o[key]);//进行递归调用
}
} else {
//其他类型
var result = o;
}
return result;
}
//调用函数进行深克隆
var newobj = deepClone(obj);
console.log(newobj);
//测试一:obj.t
console.log(obj.t == newobj.t);
//测试二:obj.t[4]
console.log(obj.t[4] == newobj.t[4]);
//测试三:obj.t[4].c
console.log(obj.t[4].c == newobj.t[4].c);
</script>
二、认识函数的上下文
2.1、this关键字(函数上下文)
重点:函数的上下文(this
)是由调用方式决定的,并不是由本身决定的。
注意:对于一个对象的函数中使用到了this,我们不要一定认为该函数就是使用的本对象的属性或其他类型,要根据函数的调用方式以及那些形式调用的相关!!!
可见下面两个函数调用的时机与情况:
<script>
var obj = {
a: 1,
b: 2,
printAB: function () {
console.log("a:" + this.a + ",b:" + this.b);
}
}
//情况一:直接调用(此时this指代的是obj对象的上下文)
obj.printAB();
//情况二:将obj对象的函数赋值给变量,此时再调用方法,log中的this就指代window对象
var printAB = obj.printAB;//此时赋值到变量的printAB的this表示window对象上下文
printAB();//此时的this.a => window.a ,this.b => window.b,由于window对象没有全局变量a,b,所以就是undefined
</script>
2.2、上下文规则
规则1:对象.方法()
,则这个函数的上下文就是打点的对象
规则1:对象打点调用它的函数方法(对象.方法()
),则函数的上下文就是这个打点的对象!
案例1:
<script>
function fn() {
console.log(this.a + this.b);
}
var obj = {
a: 66,
b: 33,
fn: fn
};
//是这个obj对象调用的,那么this上下文指的就是obj
obj.fn();//99
</script>
案例2:最具有迷惑性
<script>
var obj = {
a: 1,
b: 2,
fn: function () {
console.log(this.a + this.b);
}
};
var obj2 = {
a: 3,
b: 4,
fn: obj.fn //千万不要被迷惑成了调用obj2.fn是obj.fn调动执行的,这里仅仅只是赋值函数过来
};
//obj2调用的,那么上下对象依旧是obj2
obj2.fn();//7
</script>
案例3:
<script>
function outer() {
var a = 11;
var b = 22;
return { //匿名对象
a: 33,
b: 44,
fn: function () {
console.log(this.a + this.b);
}
};
}
//outer()返回的是指定的匿名对象,那么就是匿名对象.fn()
//上下文对象就是这个匿名对象,this.a与this.b就依次是33、44
outer().fn();//77
</script>
案例4:
<script>
function fun() {
console.log(this.a + this.b);
}
var obj = {
a: 1,
b: 2,
c: [{
a: 3,
b: 4,
c: fun
}]
};
var a = 5;
//obj.c[0]的对象是指定数组中下标为0的对象,那么此时函数的上下文就是这个下标为0的对象
obj.c[0].c();//7
</script>
规则2:函数()
,则这个函数的上下文就是window对象
规则2:函数()
,则这个函数的上下文就是window对象。
案例1:
<script>
var obj1 = {
a: 1,
b: 2,
fn: function () {
console.log(this.a + this.b);
}
};
var a = 3;
var b = 4;
var fn = obj1.fn;
//规则2:直接是函数(),那么上下文就是window,3+4=7
fn();//7
</script>
案例2:这个案例十分good
<script>
function fun() {
return this.a + this.b;
}
var a = 1;
var b = 2;
var obj = {
a: 3,
b: fun(), //规则2:直接函数(),上下文为window,则为1+2=3
fun: fun //仅仅只是引用传递
};
var result = obj.fun();//规则1:上下文为obj,则为3+3=6
console.log(result);
</script>
规则3:数组[下标]()
,则这个函数上下文就是这个数组
规则3:数组(==类数组对象==,数组[下标]()
)枚举出函数进行声明,则这个函数上下文就是这个数组。
类数组对象
:所有键名为自然数序列(从0开始),且有length属性的对象。例如arguments
对象是最常见的类数组对象,是函数的实参列表。
案例1:
<script>
var arr = ['A', 'B', 'C', function () {
console.log(this[0]);
}];
//规则3:上下文对象是arr数组,那么this指的就是这个数组,this[0]='A'
arr[3]();//'A'
</script>
案例2:
<script>
function fun() {
//规则3:arguments就是类数组,这里指代的就是多个参数
arguments[3]();//arguments类数组就是上下文,使用this就是指的是arguments类数组
}
//调用函数
fun('A', 'B', 'C', function () {
console.log(this[1]);//'B'
});
</script>
规则4:IIFE中的函数(function(){})()
,上下文是window对象
规则四:IIFE中的函数(function(){})()
,上下文是window对象。
案例1:其中包含规则1、4,闭包概念,解题的关键点一定要从上至下细细查看!!!
<script>
var a = 1;
var obj = {
a: 2,
fun: (function () {
//IIFE函数中返回值为函数,形成闭包,a会存储起来值
var a = this.a;//1、上下文对象是window,所以为1
return function () {
console.log(a + this.a);
};//2、此时fun = function(){ console.log(a + this.a); }。其中a为闭包值1,this.a由于没有调用还不确定
})() //规则4:上下文对象是window
};
//规则1:上下文对象为obj,所以this.a = 2
obj.fun();//最终执行函数:1+2 = 3
</script>
规则5:定时器延时器调用函数,上下文是window对象
规则5:定时器延时器(setInterval()
、setTimeout()
)调用函数,上下文是window对象。
对于定时器或延时器中调用函数有两种情况:
①直接传入对应的函数
<script>
var obj = {
a: 1,
b: 2,
fun: function () {
console.log(this.a + this.b);
}
};
var a = 3;
var b = 4;
//首先将obj的对象传给回调函数,此时2s后由setTimeout进行调用该函数
//规则6:定时器或延时器调用的函数上下文对象是window
setTimeout(obj.fun, 1000);//7 | window.a+window.b = 7
</script>
②在延时器的回调函数中去打点调用对象函数
<script>
var obj = {
a: 1,
b: 2,
fun: function () {
console.log(this.a + this.b);
}
};
var a = 3;
var b = 4;
//规则5:此时setTimeout的回调函数指的是其中的匿名函数,确实该函数的作用域是window
//规则1:注意在匿名函数中调用的函数方法是以对象.方法形式进行调用函数,上下文为obj
setTimeout(function(){
obj.fun();//3 | 该上下文对象是obj,所以this.a+this.b=1+2=3
}, 1000);
</script>
规则6:事件处理函数的上下文是绑定事件的dom元素
DOM元素.onclick = function(){}
:该事件处理函数的上下文就是这个DOM元素。
案例1:点击哪个盒子,哪个盒子就变红,使用同一个事件处理函数实现。(利用规则5)
<style>
* {
margin: 0;
padding: 0;
}
div {
float: left;
width: 100px;
height: 100px;
border: 1px solid #000;
margin-left: 10px;
}
</style>
<body>
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
<script>
//点击变红
function clickToColor() {
//规则6:DOM元素调用时,上下文对象为DOM元素
this.style.backgroundColor = "red";
}
//绑定单击事件,使用同一个函数
var divlists = document.getElementsByTagName("div");
divlists[0].onclick = clickToColor;
divlists[1].onclick = clickToColor;
divlists[2].onclick = clickToColor;
</script>
</body>
案例2:与案例2大致相同,不同的是,点击盒子要延迟2s才变红。
思路:在函数中使用延时器,但需要注意的是延时器中的回调函数上下文是window对象,所以我们需要先在函数中保存一份DOM元素的上下文,之后使用DOM元素上下文进行调用。
<script>
//对应html、css样式实际与案例1相同,这里就不再重复使用
function clickToColor() {
//由于setTimeout中的回调函数的上下文是window对象,若是直接使用this就是指代window对象
//所以我们需要在函数中保存对应DOM元素的上下文对象,即this
var self = this;
setTimeout(function () {
self.style.backgroundColor = "red";
}, 2000);
}
//绑定单击事件,使用同一个函数
var divlists = document.getElementsByTagName("div");
divlists[0].onclick = clickToColor;
divlists[1].onclick = clickToColor;
divlists[2].onclick = clickToColor;
</script>
规则7:call和apply方法能够自由指定上下文
引出为什么要自由指定上下文?
首先我们通过一个示例来进行说明,对于下面该案例,我们想要输出stu对象的总成绩,有什么办法能够让我们直接使用该函数打印出总成绩吗?
<script>
function sum() {
console.log("总成绩为:" + this.chinese + this.english + this.math);
}
var stu = {
chinese: 99,
english: 100,
math: 66
}
</script>
有下面两种方式进行:都是通过在内部添加或者外部添加方式接着进行调用才可以
<script>
function sum() {
console.log("总成绩为:" + (this.chinese + this.english + this.math));
}
var stu = {
chinese: 99,
english: 100,
math: 98,
//方式一:直接在对象内部添加函数
//sum: sum
}
//方式二:在外部手动为stu添加一个键值对
stu.sum = sum;
//执行方法
stu.sum();//297
</script>
为了更加方便不需要在原本对象中进行添加,就可以使用call()或apply()方法!!!
call与apply的语法
语法:
- call方法:
函数.call(上下文对象)
- apply方法:
函数.apply(上下文对象)
我们接着通过使用call、apply来解决上面案例问题:
<script>
function sum() {
console.log("总成绩为:" + (this.chinese + this.english + this.math));
}
var stu = {
chinese: 99,
english: 100,
math: 98,
}
//call()方法
sum.call(stu);//总成绩为:297
//apply()方法
sum.apply(stu);//总成绩为:297
</script>
说明:这两个方法都是能够去指定上下文对象,效果都是一致,唯一不同的是两种方式第二个参数传值不同。
call与apply不同点
介绍不同点
使用call()
或apply()
都可以进行传值给指定调用的函数作为函数的形参,其中不同点:
call()
:函数的形参需要一个个传入。apply()
:函数形参需要直接传入一个数组。- 若是两种传值与规定不符就会出现异常或者读取到的值有误!!!
apply()使用情境
对于apply()
中需要传入数组,我们在什么情况下可以进行使用呢?
- 内部函数需要获取到外部的所有形参值时,可以直接将
arguments
传入!!!
示例:
<script>
function fun1() {
//若是函数(外)中的函数(内)需要获取到函数(外)传入的参数值的话,就可以直接将arguments传入
fun2.apply(this, arguments);
}
function fun2(a, b) {
alert(a + b);
}
fun1(33, 44);
</script>
2.3、七个规则总结
规则 | 上下文 |
---|---|
对象.函数() | 对象 |
函数() | window |
数组[下标]() |
数组 |
IIFE | window |
定时器 | window |
DOM事件处理函数 | 绑定DOM的元素 |
call和apply | 可任意指定 |
三、构造函数
3.1、new 函数()
来创建对象(四步骤)
语法:new 函数()
,返回一个对象。
根据JS规定,使用new操作符来调用函数会进行四个步骤:
- 执行函数体之前会自动创建一个空白对象。===>
var obj = {};
- 此时函数的上下文(this)会指向这个对象。 ===>
this = obj;
- 函数体内的语句会一一执行。 ===> 就会使用
this
指向的这个空白对象进行一系列操作。 - 最终函数会自动返回一个上下文对象this(无论函数是否有没有return,都会返回一个对象)。
示例:
<script>
function fun() {//1、var obj = {} 2、this = obj
this.a = 5;//3.1、obj.a = 5 | 相当于新创建一个属性a
this.b = 6;//3.2、obj.b = 6 | 相当于新创建一个属性b
var m = 123;
//4、返回一个对象this(即上下文)
}
var myfun = new fun();
console.log(myfun);
</script>
3.2、认识构造函数
构造函数:使用new调用一个函数即可,任何函数都可以使用构造函数,只需要用new
调用它。
- 通常我们约定对于构造函数的函数首字母开头要大写!(并不是说首字母大写的才是构造函数,而是根据是否使用
new 函数()
方式急性调用)
示例:
<script>
//业界约定好首字母开头大写的是构造函数
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.info = function () {
console.log("name:" + name + ",sex=" + sex + ",age=" + age);
}
}
//new 函数()来创建一个对象
var changlu = new People("changlu", '男', 20);
var liner = new People("liner", '女', 23);
console.log(changlu);
console.log(liner);
changlu.info();
liner.info();
</script>
3.3、js中的"类"与实例(js没有纯粹类的概念)
Java
、C++
等是"面向对象"(oo,object-oriented)语言。
JavaScript
是"基于对象"(ob,object-based)语言。
- 在
js
中的构造函数实际上就可以类比于OO语言中的"类"。js
中没有特意书写类的格式,只能通过函数形式来模拟出类,在ES6
之中提出了class
类这个概念。js中没有纯粹的类概念,只有构造函数这个概念。
重点:
- js中使用构造函数来模拟"类"。(es6提出class概念,但是一定要记住js中没有纯粹的类概念,只有构造函数这个概念)
- 创建实例,就是通过
new 函数()
来返回一个上下文对象,也可以称为是实例。
四、原型与原型链
4.1、认识prototype属性
任何函数都有prototype
属性(意思是原型),该属性值实际上是一个对象{}
,这个对象中默认拥有constructor
属性指回函数。
作用:普通函数的prototype属性没有任何用户,而对于构造函数的prototype属性非常有用!
重点:构造函数的prototype属性是它的实例的原型。
<script>
//构造函数(其实就是一个函数)
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
//new 函数()来创建一个对象
var changlu = new People("changlu", '男', 20);
console.log(People.prototype);//输出原型
console.log(typeof People.prototype);//该函数的原型是一个对象
console.log(People === People.prototype.constructor);//这个原型对象中的constructor(函数)与我们自定义的函数相同
</script>
4.2、__proto__
与prototype属性
__proto__
:其是每个对象都有的属性。可以理解为构造器的原型!
- 简而言之将这个对象的
__proto__
属性看做是构造函数名.prototype
(也就是原型对象)
prototype
:其每个函数都有的属性。指的是原型,原型是一个对象,在这个对象中包含一个构造器属性指回函数。
<script>
//构造函数(其实就是一个函数)
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
//new 函数()来创建一个对象
var changlu = new People("changlu", '男', 20);
//对象的__proto__(指的就是其构造函数的原型)与构造函数原型比较:true
console.log(changlu.__proto__ == People.prototype);
//构造函数原型的构造函数属性与构造函数比较:true
console.log(People.prototype.constructor == People);
</script>
- 点赞
- 收藏
- 关注作者
评论(0)