面向对象更贴近我们的实际生活, 可以使用面向对象描述现实世界事物.
面向对象的思维特点:
- 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
- 对类进行实例化, 获取类的对象
1.对象
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的
- 属性:事物的特征,在对象中用属性来表示
-
2.类
在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某一大类(class)
- 对象特指某一个,通过类实例化一个具体的对象
2.1创建类
class name {// class body}
var XX = new name();
注意:类必须使用new 实例化对象
2.2构造函数
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义, 类内部会自动给我们创建一个constructor()
class Star{constructor(name,age){this.name=name;this.age=age;}}var hg=new Star('胡歌',18)var xs=new Star('许嵩',20)console.log(hg)console.log(xs)
通过 class 关键字创建类,类名我们还是习惯性定义首字母大写
类里面有个 constructor函数,可以接收传递过来的参数,同时返回实例对象
constructor函数只要 new 生成实例,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
最后注意语法规范
创建类➡类名后面不要加小括号
生成实例➡类名后面加小括号
构造函数不需要加 function 关键字
2.3类中添加方法
class Person{constructor(name,age){this.name=name;this.age=age;}say(){console.log(this.name+'你好')}}var person=new Person('胡歌',18)person.say()
- 类的共有属性放到constructor 里面
- 类里面的函数都不需要写 function 关键字
2.4类的继承
// 父类class Father {}// 子类继承父类class Son extends Father {}
class Father{constructor(x,y){this.x=x;this.y=y;}sum(){console.log(this.x+this.y)}}class Son extends Father{constructor(x,y){super(x,y)this.x=xthis.y=y}sub(){console.log(this.x-this.y)}}var son=new Son(5,3)son.sum()son.sub()
2.5super关键字
super 关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数
2.5.1调用父类的构造函数
class Person{constructor(name){this.name=name}}class Student extends Person{constructor(name,age){super(name)this.age=age}}
2.5.2调用父类的普通函数
class Father{say(){return '我是爸爸'}}class Son extends Father{say(){return super.say()+'儿子'}}var son = new Son()console.log(son.say())
- 多个方法之间不需要添加逗号分隔
- 继承中属性和方法的查找原则:就近原则,先看子类,再看父类。
3.构造函数和原型
在典型的 OOP 的语言中(如 Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在 ES6之前, JS 中并没用引入类的概念。
ES6, 全称 ECMAScript 6.0 ,2015.06 发版。但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。
在 ES6之前 ,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象有三种方式
- 对象字面量
- new Object()
- 自定义构造函数
```javascript function Star(name,age){ this.name=name this.age=age this.sing=function(){var obj1=new Object();var obj2={}
} }console.log('唱歌')
var hg = new Star(‘胡歌’,18)
注意:1. 构造函数用于创建某一类对象,其首字母要大写1. 构造函数要和new一起使用才有意义<a name="LeFdz"></a>## 3.1静态成员和实例成员- 静态成员: 在构造函数本身上添加的成员为静态成员,只能由构造函数本身来访问- 实例成员: 在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问```javascriptfunction Star(name,age){this.name=namethis.age=agethis.sing=function(){console.log(name+'唱歌')}}var hg = new Star('胡歌',18)hg.sing()console.log(Star.age) // undefinedStar.sex='男'//静态成员就是在构造函数本身上添加的成员 sex 就是静态成员console.log(Star.sex)console.log(hg.sex) //// undefined 不能通过对象来访问
3.2构造函数的问题
构造函数方法很好用,但是存在浪费内存的问题。
我们希望所有的对象使用同一个函数,这样就比较节省内存
3.3构造函数原型 prototype
构造函数通过原型分配的函数是所有对象所共享的,这样就解决了内存浪费问题
JavaScript 规定,每一个构造函数都有一个prototype属性,指向另一个对象,注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
我们可以把那些不变的方法,直接定义在prototype 对象上,这样所有对象的实例就可以共享这些方法
function Star(name,age){this.name=namethis.age=age// this.sing=function(){// console.log(name+'唱歌')// }}//公共的方法我们放到原型对象身上Star.prototype.sing=function(){console.log(this.name+'唱歌')}// 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上var hg=new Star('胡歌',18)var xs=new Star('许嵩',18)console.log(hg.sing === xs.sing) //truehg.sing()xs.sing()
3.4对象原型 proto
对象都会有一个属性 proto 指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype 原型对象的属性和方法,就是因为对象有proto原型的存在。
proto对象原型和原型对象 prototype 是等价的
proto对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
console.log(xs.__proto__===Star.prototype) //true
3.5constructor 构造函数
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
一般情况下,对象的方法都在构造函数(prototype)的原型对象中设置
function Star(name,age){this.name=namethis.age=age// this.sing=function(){// console.log(name+'唱歌')// }}// Star.prototype.sing=function(){// console.log(this.name+'唱歌')// }Star.prototype={// // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数constructor:Star,sing:function(){console.log(this.name+'唱歌')},movie:function(){console.log(this.name+'演戏')}}var hg=new Star('胡歌',18)hg.sing()hg.movie()
3.6原型链查找规则
function Star(name,age){this.name=namethis.age=age// this.sing=function(){// console.log(name+'唱歌')// }}// Star.prototype.sing=function(){// console.log(this.name+'唱歌')// }Star.prototype={// // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数constructor:Star,sing:function(){console.log(this.name+'唱歌')},movie:function(){console.log(this.name+'演戏')}}var hg=new Star('胡歌',18)hg.sing()hg.movie()// 1. 只要是对象就有__proto__ 原型, 指向原型对象console.log(Star.prototype)// 2.我们Star原型对象里面的__proto__原型指向的是 Object.prototypeconsole.log(Star.prototype.__proto__)console.log(Star.prototype.__proto__===Object.prototype)// 3. 我们Object.prototype原型对象里面的__proto__原型 指向为 nullconsole.log(Object.prototype.__proto__)
4.继承
S6 之前并没有给我们提供extends继承
-
4.1call()
调用这个函数,并且修改函数运行时的 this 指向
fun.call(thisArg,arg1,arg2,......)
thisArg:当前调用函数 this 的指向对象
- arg1,arg2: 传递的其他参数 ```javascript function fn(x,y){ console.log(‘helloworld’) console.log(this) console.log(x+y) } var o={ name:’cheng’ }
// fn(1,2) //call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象 fn.call(o,1,2)
<a name="bdaSS"></a>## 4.2借用构造函数继承父类型属性核心原理: 通过 call() 把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性```javascript//父构造函数function Father(name,age){this.name=namethis.age=age}//子构造函数function Son(name,age,score){//this 指向子构造函数的对象实例Father.call(this,name,age)this.score=score}var son=new Son('胡歌',18,100)console.log(son)
4.3借用原型对象继承父类型方法
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法
//父构造函数function Father(name,age){this.name=namethis.age=age}Father.prototype.money=function(){console.log(100000)}//子构造函数function Son(name,age,score){//this 指向子构造函数的对象实例Father.call(this,name,age)this.score=score}// var son=new Son('胡歌',18,100)// console.log(son)//Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化Son.prototype=new Father();//如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数Son.prototype.constructor=Son;//这个是子构造函数专门的方法Son.prototype.exam=function(){console.log('考试')}
4.4 类的本质
class 本质还是 function
类的所有方法都定义在类的 prototype属性上
类创建的实例,里面也有proto指向类的prototype原型对象
所以 ES6 的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
所以 ES6 的类其实就是语法糖
语法糖:语法糖就是一种便捷写法,简单理解
[
](https://blog.csdn.net/Augenstern_QXL/article/details/115219073)
5.ES5新增方法
ES5 给我们新增了一些方法,可以很方便的操作数组或者字符串
- 数组方法
- 字符串方法
-
5.1数组方法
迭代(遍历)方法:foreach() ,map(),filter(),some() ,every() ;
5.1.1forEach()
array.forEach(function(currentValue,index,arr))
var arr=[1,2,3]var sum=0//forEach 迭代(遍历) 数组arr.forEach(function(value,index,array){console.log('每个数组元素' + value);console.log('每个数组元素的索引号' + index);console.log('数组本身' + array);sum+=value})console.log(sum)
5.1.2filter()筛选数组
array.filter(function(currentValue,index,arr))
var arr=[1,2,3,4,5,6]// filter 筛选数组var newArr=arr.filter(function(value,index){return value % 2===0})console.log(newArr)
5.1.3some()
some()方法用于检测数组中的元素是否满足指定条件(查找数组中是否有满足条件的元素)
- 注意它返回的是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false
如果找到第一个满足条件的元素,则终止循环,不再继续查找
var arr=['red','blue','green']//some 查找数组中是否有满足条件的元素var flag=arr.some(function(value){return value === 'green'})console.log(flag)
5.2字符串方法
trim()方法会从一个字符串的两端删除空白字符
- trim()方法并不影响原字符串本身,它返回的是一个新的字符串
var str = ' hello world 'console.log(str)var str1 = str.trim()console.log(str1)
5.3对象方法
5.3.1Object.keys()
- Object.keys()用于获取对象自身所有的属性
- 效果类似for…in
- 返回一个由属性名组成的数组 ```javascript var obj={ id:1, name:’小米’, price:1999, num:0
} var arr=Object.keys(obj) console.log(arr) arr.forEach(function(value,index){ console.log(value) console.log(index) })
<a name="XeWBC"></a>### 5.3.2Object.defineProperty()Object.defineProperty()定义对象中新属性或修改原有的属性(了解)```javascriptObject.defineProperty(obj,prop,descriptor)
- obj : 目标对象
- prop : 需定义或修改的属性的名字
- descriptor : 目标属性所拥有的特性 ```javascript var obj={ id:1, name:’小米’, price:1999, num:0
} console.log(obj) //Object.defineProperty() 定义新属性或修改原有的属性 Object.defineProperty(obj,’num’,{ value:1000, enumerable: true }) console.log(obj) Object.defineProperty(obj,’price’,{ value:9.9,
}) console.log(obj) Object.defineProperty(obj, ‘id’, { //如果值为false 不允许修改这个属性值 默认值也是false writable: false, }); console.log(obj)
```javascriptObject.defineProperty(obj,'address',{value:'china',// 如果只为false 不允许修改这个属性值 默认值也是falsewritable:true,// enumerable 如果值为false 则不允许遍历, 默认的值是 falseenumerable:false,// configurable 如果为false 则不允许删除这个属性//不允许在修改第三个参数里面的特性 默认为falseconfigurable:false})console.log(obj)
第三个参数 descriptor 说明:以对象形式{ }书写
value:设置属性的值,默认为undefined
writeable: 值是否可以重写 true | false 默认为false
enumerable: 目标属性是否可以被枚举 true | false 默认为false
configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false
6.函数进阶
6.1函数的定义方式
// 1. 自定义函数(命名函数)function fn(){}// 2. 函数表达式 (匿名函数)var fun=function(){}//利用 new Function('参数1','参数2', '函数体');//Function 里面参数都必须是字符串格式,执行效率低,较少写var f=new Function('a','b','console.log(a+b)')f(1,2)console.dir(f)console.log(f instanceof Object) //true
6.2函数的调用方式
//普通函数function fn(){console.log('普通函数')}fn()//对象的方法var o={say:function(){console.log('对象的方法')}}o.say()//构造函数function Star(){};new Star()
6.3改变函数内部this指向
JavaScript 为我们专门提供了一些函数方法来帮我们处理函数内部 this 的指向问题
常用的有 bind(),call(),apply()三种方法
6.3.1call() 方法
var o={name:'cheng'}function fn(a,b){console.log(this)console.log(a+b)}fn.call(o,1,2)
function Father(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}function Son(name, age, sex,score) {Father.call(this,name,age,sex)this.score=score}var xs= new Son('许嵩', 18, '男',100);console.log(xs);
6.3.2apply()方法
apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
- fun.apply(thisArg,[argsArray])
- thisArg: 在 fun 函数运行时指定的 this 值
- argsArray : 传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
var max = Math.max.apply(null, arr);
var arr=[1,2,3,4,5,6]var max=Math.max.apply(Math,arr)console.log(max) //6(max)
6.3.3bind()方法
fun.bind(thisArg,arg1,arg2,….)
- 返回由指定的 this值和初始化参数改造的 原函数拷贝
- 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind ```javascript var o = { name: ‘cheng’ };
function fn(a, b) { console.log(this); console.log(a + b);
}; //当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind var f=fn.bind(o, 1, 2); f()
var o={ name:’cheng’ } function fn(a,b){ console.log(this) console.log(a+b) } //自动调用函数 fn.call(o,1,2)
6.3.4总结
相同点:
- 都可以改变函数内部的 this指向
区别点:
- call和apply会调用函数,并且改变函数内部的this指向
- call和apply传递的参数不一样,call 传递参数,apply 必须数组形式
- bind不会调用函数,可以改变函数内部this指向
主要应用场景
- call经常做继承
- apply经常跟数组有关系,比如借助于数学对线实现数组最大值与最小值
- bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向
