Day 8
面向对象编程
编程需要养成面向对象的思维。
对象的作用:封装信息。
对象具有特征和行为(属性和方法)
- 特征:用于描述对象
- JS中有特定的语法来描述(就是两种创建对象的方法,见下)
- 方法
例:math对象的属性就是PI:3.1415926,方法就是函数,如abs()、max()等等
- 属性就是一个key:value(只要不是函数就是属性
- 方法就是函数
对象在内存中的存放和基本数据类型不一样
-
基本数据是直接存放在“栈内存”中
-
而对象存放的是内存地址,地址指向“堆”的区域
对象的分类
- 内置对象:ES标准定义的对象,如:Math、Boolean等(一切皆对象)
对象的基本操作
创建对象的几种方式:
-
构造函数创建对象方式
-
<script> var student = new Object() // 创建对象 student.name = "坤坤"; // 定义属性 student.like = "唱跳rap篮球"; student.gift = function(){ // 定义行为(方法) console.log(student.name + "鸡你太美"); } console.log(student); </script> -
字面量的方式创建对象
-
<script> var teacher = { name:"老李", //属性 age:"23", //特别注意是逗号 gift:function(){ // key:函数对象 console.log(this.age + "岁,是老师desu~!"); } } </script>
-
字面量对象
属性key的value也可以是对象,然后另一个对象再建个属性的value是原来的对象,也即可以不断地对象下去,也就是循环
设置对象value的方法就是直接key:对象名称
获取对象中的属性
- 可以直接对象.属性名
- 简单,效率高
- 也可以用[],如teacher["name"](注意,引号是直接获取属性时用,不加引号是获取变量)
- 不知道具体要获取什么属性的时候,而是定义了变量=属性的时候,要获取这个变量的属性怎么办?
- 肯定不能用对象.变量了,这变量会被认为是属性
- 所以就只能用console.log(对象[变量])
- 注意没有引号
- 还有就是可以在遍历属性中用到,见下
修改属性值
对象.属性 = 新值
对象["属性名"] = 新值
删除对象的属性
delete 对象.属性
delete 对象[“属性”]
检查对象中是否有这个属性(在不在对象中
- 使用in运算符
- “属性” in 对象,返回true即为在对象中
- 对象.hasOwnProperty("属性")
遍历对象中的属性
for in循环
如何获取该对象的所有属性?
for(var item in student){
console.log(student[item]) //要遍历属性,肯定不是单独某个属性,所以只能是[]
}
数据类型的区别
基本数据类型和引用数据类型(对象)到底有什么区别呢
主要还是和内存中的存放方式不一样的区别
-
基本数据类型中,在a = b的情况后,只修改a的话是不影响b的,相互独立的
-
但是在对象中,由于a、b实际上等同的是堆区域的内存地址,所以只要堆发生了变化,这两个对象都会发生变换,而且是一样的内容。
// 基本数据类型
var a = 23;
var b = a;
a++;
console.log(a); // 打印结果:24,a在内存的栈区域中直接被修改了
console.log(b); // 打印结果:23
// 引用数据类型
<script type="text/javascript">
var a = {
name:"老王"
}
var b = a;
a.name = "小明";
console.log(a.name) // "小明"
console.log(b.name) // "小明"
console.log(a) //{name:"小明"}
console.log(b) //{name:"小明"}
</script>
this指向
this指向的是一个对象,但是根据函数的调用方式的不同,this会指向不同的对象
var student = {
name:"小明",
like:function(){ //匿名函数
console.log(this) //this在这里指的是当前这个对象
console.log(this.name)
console.log(this.name+"喜欢打篮球")
}
}
var fn = student.like
var fn1 = function(){
console.log(this)
console.log(this.name)
console.log(this.name+"喜欢打篮球")
}
如果这时候在控制台输入fn == fn1,回事true吗?
- 不会。因为like的内存地址和fn1的内存地址是不一样的,虽然内容一样。但也能发现like和fn的内存地址是一样的。
然后此时再输入student.like()和fn(),会发现两者输出的console.log(this)不一样:
- 前者是student这个对象,后者是window,也就是**“根据函数的调用方式的不同,this会指向不同的对象"**,可知:
- 以方法的形式调用时,this是调用方法的那个对象
- 以函数的形式调用时,this永远都是window。比如
fun();相当于window.fun();- 怎么理解这个呢?student.like()的this指向的是student对象这个好理解。那么fn是全局变量,实际上就是window.fn()的this,指向的自然就是window咯,可以发现规律就是是谁的就指向谁。
- 这个术语/行为也就是:解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象。
类数组arguments(可略过)
实际上,在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象 this
- 封装实参的对象 arguments
arguments是一个类数组对象,它可以通过索引来操作数据,也可以获取长度。
arguments代表的是实参。在调用函数时,我们所传递的实参都会在arguments中保存。有个讲究的地方是:arguments只在函数中使用。
-
arguments.length:可以获取实参的长度
-
arguments.callee():可以获取函数名称
改变this的指向
1、call和apply
通过this指向那个例子可以看出fn()的this指向的是window,那么此时再加一个对象teacher(也在上面),可否将this指向teacher呢?可以,用使用call和apply来改变
在控制台输入fn.call(teacher)或者fn.apply(teacher),此时原本输出的是小明喜欢打篮球就,变成了老李喜欢打篮球。也就是this指向的对象修改了。
call和apply有什么区别?(两都是有顺序的)
传参数的不同。call只能依次传,而apply用的是数组
var student = {
name:"小王",
like:function(){
console.log(this)
console.log(this.name+"喜欢打篮球!")
for(var i = 0;i<arguments.length;i++){ //这样可以传参了
console.log(this.name+"喜欢"+arguments[i])
}
}
}
var teacher = {
name:"老王",
}
var fn = student.like
fn.call(teacher)
student.like.call(teacher,“唱”,“跳”,“rap”) //call只能依次传,多个参数
fn.app(teacher)
student.like.apply(teacher,[“唱”,“跳”,“rap”]) //apply可以用数组,一个参数
2、bilnd改变this指向
永远绑定某个对象
var student = {
name:"小王",
like:function(){
console.log(this.name+“喜欢CTRL")
}
}
}
var fn = function(){
console.log(this)
like:function(){
console.log(this.name+“喜欢CTRL")
}
}.blind(student)
console.log(fn()),本来应该是window的,但是由于加了blind,this的指向就变成student了
创建自定义对象
1、字面量(之前已说过不再赘述)
var student = {
name:"小明",
age:16,
school:"尚学堂",
like:function(){
console.log(this.name+"喜欢篮球")
}
}
2、如何批量创建对象?工厂模式,就像流水线一样
function students(name,age,gift){
var student = {
name:name,
age:age,
gift:function(){
console.log(this.name+"的特长是"+gift);
}
}
return student;
}
var s1 = students("小红",11,"钢琴") //这样就可以批量造对象了
console.log(s1);
var s2 = ...
3、构造函数
关键在于new关键字
function teachers(name,age,teach){
this.name:name, //this的作用是指向new的对象
this.age:age,
this.gift:function(){
console.log(this.name+"的科目是"+gift);
}
console.log(this)
}
var t1 = new teachers("大黄",22,"前端") //这里的new就是构造 新对象,将函数内部的this指向new的对象,否则是window
console.log(t1.name)
4、class创建对象(ES6新规范)
和构造函数一样,只是function换成class
思考:这几种方式,每次创建对象,内容其实都是一样的(gift),但每次都要重新创建,在内存的堆区域都要新开辟一块,不是浪费内存吗?
所以就可以共用呀,只要引用同意给内存地址不就好了,那不就是将gift专门创建一个对象不就好了。然后其他对象引用这个地址就OK。
var giftshare = function(){
console.log(this.name+gift)
}
function students(name,age,gift){
var student = {
name:name,
age:age,
gift:giftshare
}
return student;
}
function teachers(name,age,teach){
this.name:name, //this的作用是指向new的对象
this.age:age,
this.gift:giftshare
console.log(this)
}
然后再控制台对比student.gitf == teachers.git是否为true
但如果有很多不一样的,一个个抽出去,岂不是还是很麻烦?
这就有原型的概念了