面向对象的编程思想
面向对象是利用对象进行编程的一种思想, 面向对象的编程思想是相对于面向过程的编程思想而言。
在面向对象的程序设计(英语:Object-oriented programming,缩写:OOP)中, 对象是一个由信息及对信息进行处理的描述所组成的整体, 是对现实世界的抽象. 在现实世界里我们所面对的事情都是对象, 如计算机、电视机、自行车等。
ECMAScript有两种开发模式: 1.面向过程, 2.面向对象(OOP)。
面向对象编程(OOP)的三个基本特性
-
封装:将属性和方法(数据和功能)封装在一起。
-
继承:继承它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
-
多态:允许让父类的指针分别指向不同的子类, 调用不同子类的同一个方法, 会有不同的执行效果。多态表现形式有重写Override和重载Overload。JS中可以使用多态的重写,子类重写父类的方法或是属性。重载是指同一个类里的方法可以有多种实现,同名(方法名、函数名)不同参。
对象的组成
属性(对象的属性) ——变量:状态、静态的
方法(对象的行为) ——函数:过程、动态的
面向过程和面向对象
面向过程:
就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现, 使用的时候一个一个依次调用就可以了。
面向对象:
是把构成问题的事务分解成各个对象,每个对象都有自己独立的属性和行为, 对象可以将整个问题事务进行分工, 不同的对象做不同的事情, 这种面向对象的编程思想由于更加贴近实际生活, 所以被计算机语言广泛应用。
构造函数
使用了new来创建对象的函数就是构造函数。
//ES6 还没出来之前,就遇到了一些问题,创建的对象类型不明确
// 采用构造函数来解决
// 构造函数创建对象
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person("刘亦菲", 18);
function Dog(name, age) {
this.name = name;
this.age = age;
}
var d = new Dog("旺财", 3)
console.log(p);//Person {name: '刘亦菲', age: 18}
console.log(d);//Dog {name: '旺财', age: 3}
构造函数和普通函数的区别
构造函数与普通函数的唯一区别在于调用它们的方式, 任何函数, 只要通过new操作符来调用, 就可作为构造函数; 而任何函数, 若不通过new操作符调用, 就是普通函数。
new操作符做了什么
- 在构造函数内部(隐式)创建一个空对象
- 空对象的指针(__proto__)指向构造函数的原型(prototype)
- 构造函数的this指向空对象,给空对象添加(自定义的)属性和方法
- 隐式的返回 return this
function Person(name,age){
//new操作符做了什么
//1.在构造函数中创建一个空对象
var o=new Object();
//2.空对象的指针指向构造函数的原型
o.__proto__=Person.prototype;
//3.给空对象添加属性,挂载属性和方法
o.name=name;
o.age=age;
//4.构造函数内部的this指向空对象,不再指向window
//5.隐式返回this,由于this指向o,相当于返回o
return o;
}
var p1=Person("刘亦菲",18);//没有使用new关键字,出来的结果和构造函数new出来的一样
console.log(p1);//Person {name: '刘亦菲', age: 18}
匿名函数
1. 匿名函数就是没有函数名字的函数
var aa=function(){
console.log("aa");
}
aa();//调用匿名函数,这里的aa是一个变量,相当于函数的名称
函数中包含匿名函数
function bb(){
return function(){//匿名函数
console.log("cc");
}
}
bb()();//bb()函数返回一个匿名函数,后面再加一个小括号执行匿名函数。
2.匿名函数的自运行
匿名函数的自运行, 不需要主动调用, 会直接执行
(function(msg){//形参
console.log(msg);
})("我是自运行函数,不需要调用自己就会执行");//实参
闭包(Closure)
闭包是这样的一种机制:函数嵌套函数,内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制所回收。
function aa() {
var a = 5;
return function () {
a++;
console.log("a=" + a);
}
}
// console.log(a); //无法直接访问a Uncaught ReferenceError: a is not defined
var cc = aa(); //aa函数被执行了, 并返回了内部的匿名函数给cc
//a一直在内存中没有被释放
cc(); //a=6
cc(); //a=7
cc(); //a=8
闭包的好处
- 可以让一个变量长期的驻扎在内存中不被释放,在IE6、7、8下会存在内存溢出
- 避免全局变量污染,和全局变量不同,闭包中的变量无法被外部使用
闭包的用途
- 实现缓存
- 存储值防止全局变量污染
- 实现函数科里化
- 节流和防抖
使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。
- 闭包会在父函数的外部,改变父函数内部变量的值。
垃圾回收机制GC
JS引擎会在一定的时间间隔来自动对内存进行回收(把内存释放)
JS垃圾回收机制有两种:
- 标记清除:js会对变量做一个标记Yes or No的标签以供js引擎来处理, 当变量在某个环境下被使用则标记为yes, 当超出该环境(可以理解为超出作用域)则标记为no, js引擎会在一定时间间隔来进行扫描, 会对有no标签的变量进行释放(将该变量所占的内存释放掉)。
- 引用计数:对于js中引用类型的变量, 采用引用计数的内存回收机制, 当一个引用类型的变量赋值给另一个变量时, 引用计数会+1, 而当其中有一个变量不再等于值时, 引用计数会-1, 如果引用计数为0, 则js引擎会将其释放掉。
this关键字
JS中的this指的是当前对象,this在不同的代码环境下代表的对象是不一样的。
- 情况一:此时this指的是此刻正在运行的函数所依附的对象
var btn=document.querySelector("button");
//按下按钮
btn.onclick=function(){
console.log(this);
}
2. 情况二:定时器中的this
定时器setInterval()中的匿名函数是在被全局中的window对象调用的, 所以里面的this指的就是window对象, 而不是button。
var btn = document.querySelector("button");
btn.onclick = function () {
console.log(this); //button对象
setInterval(function () {
console.log("setInterval:", this); //window对象
}, 2000);
setTimeout(function () {
console.log("setTimeout:", this); //window对象
}, 3000);
}
3. 情况三:构造函数中的this
在构造函数中的this是使用new关键字创建的那个对象。
function Person(name){
this.name=name;
this.show=function(){
console.log(this);
console.log(this.name);
}
}
new Person("刘亦菲").show();
4. 情况四:箭头函数的this
箭头函数的this指向上下文对象,它本身没有this,利用bind、call、apply都无法改变this。
var arrowFn1 = () => {
console.log("arrowFn1", this);//window
}
arrowFn1();
console.log("--------------------------------------------------------------------------------------------------");
var btn = document.querySelector("button");
//按下按钮
btn.onclick = function () {
let arrowFn2 = () => {
console.log("arrowFn2", this);//button对象
}
arrowFn2();
}
5. 情况五:普通函数中的this
this指的就是window对象。
function fn(){
console.log(this);//window
}
fn();