第8天

152 阅读6分钟

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.logthis.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

但如果有很多不一样的,一个个抽出去,岂不是还是很麻烦?

这就有原型的概念了