🌟原型:
通过工厂函数与构造函数的方式实例化对象,每实例化一个对象就会重新开辟一片内存空间,造成空间浪费,把公共的属性和方法放在构造函数的原型上,并不会重新开辟空间,节省内存。
原型是构造函数的属性。构造函数.prototype={}
放到原型上的方法与属性,在对象的_ proto _中找到。
🌟原型链:本身->构造函数->构造函数的原型->原型的构造函数的原型->->
访问对象的属性或方法时遵循的链式规则,该属性或方法会在对象本身调用,对象本身没有则去对象本身的构造函数调用,本身构造函数没有则去父类的构造函数调用、父类的原型…以此类推,直到寻找至Object、以及Object的原型、null。最后属性不存在时会得到undefined,方法不存在则会报错。
ject.prototype.say=function(){ alert("Object的方法"); } function person(){ this.say=function(){ alert("person的方法"); } } person.prototype.say=function(){ alert("person原型的方法"); } function student(){ this.say=function(){ alert("student的方法"); } } student.prototype=new person(); var xiaoming=new student(); xiaoming.say=function(){ alert("xiaoMing的方法"); } xiaoming.say();
🌟对象的继承
继承:一个对象拥有另一个对象的属性与方法
- 父类(基类):被继承的对象,
- 子类:继承的对象。
优点:
提高代码的重用性,提高代码的逻辑性与可维护性。
🌟继承方式
- 原型继承(将父类的实例作为子类的原型)
原理:对象访问属性方法会遵循 "构造函数 -> 原型"的顺序,所以将父类的实例放到子类原型时, 子类实例化出的对象就可以访问到原型上父类的内容,从而实现了继承。
function Animal(){ this.age=age; this.eat=function(food){ alert(food) } } function Cat(){ } Cat.prototype = new Animal(); // 将父类(Animal)的实例作为子类(Cat)的原型 var tom = new Cat(); cat.eat('fish') // 弹出 'fish' console.log(tom instanceof Animal); //true console.log(tom instanceof Cat); //true
call FN.call(obj,参数1,参数2....)
格式:fun.call(obj2,参数1, 参数2…)
本质上来说,call方法实际上就是要改变fun函数内的this指向。
function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.call(cat, "鱼") // 继承单个方法(改变单个方法的this指向) // 继承整个构造函数 (改变整个构造函数的this指向) Animal.call(cat);//(方法立即执行) cat.say("鱼")
apply
用法基本与call相同,函数的参数通过数组传递
格式:fun.apply(obj2,[参数1, 参数2…])
function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.apply(cat, ["鱼"]) Animal.apply(cat) cat.eat("鱼")
bind继承
bing
用法与call
类似,也是可以改变函数体内 this 的指向- call与apply是使用时立即执行函数,
bind
是返回对应函数,便于稍后调用
function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.bind(cat, "鱼")() // bind只是改变了this指向,想要调用该函数还需()
- ES6类继承
通过
extends
关键字实现类与类之间的继承,然后实例化子类,来实现继承。详见下一节
🌟继承的顺序
优先级:对象本身–>构造函数–>原型链
原型链:
当访问对象的属性或方法时,该属性或方法会在对象本身调用,对象本身没有则去对象本身的构造函数调用,本身构造函数没有则去父类的构造函数调用、父类的原型…以此类推,直到寻找至Object、以及Object的原型、null。最后属性不存在时会得到undefined,方法不存在则会报错。
Object.prototype.say=function(){ alert("Object的方法"); } function person(){ this.say=function(){ alert("person的方法"); } } person.prototype.say=function(){ alert("person原型的方法"); } function student(){ this.say=function(){ alert("student的方法"); } } student.prototype=new person(); var xiaoming=new student(); xiaoming.say=function(){ alert("xiaoMing的方法"); } xiaoming.say();
🌟this
this是一个很特别的关键字,被自动定义在所有函数的作用域中。
this总是会指向一个对象。或者说,this就是属性或方法‘当前’所在的对象。
🌟this的指向
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
- 在全局环境和全局函数中this指
window
全局对象。 - 作为对象方法调用,this 指代调用该方法对象。
- 在构造函数中this指向构造函数的
实例
。 - 在事件中,this指向事件源。
- 在call和apply中,this指的是方法中传入的对象,如果apply中没有传对象,this指向window
function Fun(){ run(){ console.log(this) // this指向实例化出的对象,因为是实例化出的对象调用了run方法 setInterval(_=>{ conosle.log(this) // this指向 window,因为是window调用了setInterval方法 }) } }
🌟改变this的指向
- call() ;
- 第一个参数是this的指向,第二个参数为一个参数列表,会立即执行
- apply() ;
- 第一个参数是this的指向,第二个参数为一个参数数组,会立即执行
- bind()
- 第一个参数是this的指向,第二个参数为一个参数列表,不会立即执行
🌟ES6对象扩展
🌟属性方法的简洁表示法
当属性名为变量名, 属性值为变量的值时,可直接将变量作为对象的属性:
let username = "张三" let obj = { username, // 属性名为变量名, 属性值为变量的值 age: 20 }
对象方法的简写:
let obj = { run:function(){ alert(1) } } // 简写为: let obj = { run(){ alert(1) } }
这种写法用于函数的返回值,将会非常方便。
function getPoint() { const x = 1; const y = 10; return {x, y}; } getPoint()
🌟对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" let { first: f, last: l } = { first: 'hello', last: 'world' }; f // 'hello' l // 'world'
上面代码中,foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。
如果变量名与属性名一致,可以写成下面这样:
let { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
- 用途1:接收函数的参数
// 接收被除数x与除数y function calc({dividend: x, dividend: y}){ return x/y } let divide = calc({dividend:9,dividend:3})
用途2:接收函数的返回值
function calc(a,b){ return { add: a+b, subtract: a-b, multiply: a*b, divide: a/b } } // 相对于返回数组来讲,不需要知道返回值参数的顺序 let {add, subtract, multiply, divide} = calc(1,2)
🌟对象的Rest属性
ES6中数组的rest和扩展运算符,用于数组于普通参数之间的转化
Math.max(...[1,2,3,4]) function fun(a,b,...c){ }
现在在ES9中,当对象结构复制时也可以使用rest运算符:
let obj = {a: 1, b: 2, c: 3}; let {a,...x} // a == 1 // x == {b: 2, c: 3}
或在函数参数中也可以使用:
restParam({ a: 1, b: 2, c: 3 }); function restParam({ a, ...x }) { // a = 1 // x = { b: 2, c: 3 } }
🌟写在最后
更多JavaScrip知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!