文章目录
1、构造函数
构造函数也是一个普通函数,唯一的区别是调用方式不一样(构造函数习惯上首字母大写)
构造函数主要用来初始化对象,可以把对象中一些公共的属性和方法抽取处理,然后封装到函数里面
在全局上下文中,函数调用中直接使用this,this指向window
构造函数——this指向构造出的对象
// 构造函数
function Star(uname,age) {
this.uname = uname
this.age = age
this.sing = function() {
console.log("唱歌")
}
}
// foo为当前实例的对象(构造函数绑定的对象为当前实例的对象,this指向构造出的对象)
var foo = new Star('zyzhang',18)
console.log(foo)
// 补充:除了使用构造函数创建对象,常见的还要利用new Object()创建对象 和 利用对象字面量创建对象
1.1、静态成员和实例成员
实例成员就是构造函数内部通过this添加的成员(如:uname、age、sing就是实例成员)
实例成员只能通过实例化对象来访问(如foo.name 而Star.name 是不允许的)
静态成员在构造函数本身上添加的成员
静态成员只能通过构造函数来访问(如:Star.age 而foo.age是不允许的)
构造函数存在的问题:
构造函数方法很好用,但是存在浪费内存的问题
如:我们实例化多个对象,上面的构造函数中的sing函数会开辟多个内存空间
如果我们希望所有对象使用同一个函数,该怎样做呢??? =====》 原型对象
1.2、构造函数原型 prototype
prototype属性是函数独有的,从一个函数指向一个对象,它的含义是函数的原型对象,也就是这个函数所创建的实例的原型对象。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
于是我们的代码就可以这样写了:
function Star(uname,age) {
this.uname = uname
this.age = age
}
Star.prototype.sing = function() {
console.log("唱歌")
}
var foo = new Star('zyzhang',18)
var foo2 = new Star('zyzhang2',19)
foo.sing() foo2.sing()
1.3、总结
原型是什么? 一个对象,也称为prototype为原型对象
原型对象的作用是什么? 共享方法
疑问??? 上面的sing函数是定义在Star这个构造函数的原型对象(prototype)身上,为什么上面的foo这个实例对象可以使用这个方法呢?
这是因为foo这个实例对象身上也有一个对象叫__proto__
2、对象原型__proto__
__proto__属性是对象所独有的;
__proto__属性都是由一个对象指向一个对象(指向它们的原型对象,也可以理解为父对象)
每一个对象(除null)都会有的属性__proto,这个属性指向该对象的原型。
即 foo. proto === Star.prototype
方法查找规则:首先看foo对象身上是否有sing方法,有就执行这个对象上的sing,如果没有,因为 __proto__的存在,就去构造函数原型对象prototype身上去找sing这个方法。
3、constructor 构造函数
对象原型( proto)和构造函数原型对象(prototype)里面都有一个属性constructor属性,constructor也称之为构造函数,因为它指向构造函数本身
constructor属性也是对象所独有的;
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数,如下实例:
function Star(uname,age) {
this.uname = uname
this.age = age
}
Star.prototype = {
// 如果修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor这个属性指回原来的构造函数
constructor: Star,
sing: function() {
},
movie: function() {
}
}
var foo = new Star('zyzhang',18)
var foo2 = new Star('zyzhang2',19)
foo.sing() foo2.sing()
4、三者之间的关系
5、原型链
我们关注一下上面的Star原型对象,也是一个对象,那么Star.prototype__proto__指向的是什么呢?
通过打印我们发现 我们的Star原型对象里面的__proto__原型指向的是Object.prototype
具体通过如下图了解具体内容(图片来源于网络)
6、JavaScript的成员查找机制(规则)
1)当访问一个对象属性(包括方法)时,首先查找这个对象自身有没有该属性
2)如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
3)如果还没有就查找原型对象的原型(Object的原型对象)
4依次类推一直找到Object为止(null)
7、原型对象this指向
1、构造函数中里面this指向的是实例对象(foo)
2、原型对象函数里面的this指向的是实例对象(foo)
8、拓展内置对象
我们知道Array.prototype里面有很多方法(如:pop,push,sort等等)但是没有求和 的方法,这时我们就可以通过数组的原型对象自定义一个方法(如sum)
Array.prototype.sum = function() {
var sum = 0;
for(var i = 0; i< this.length;i++) {
sum += this[i]
}
return sum;
}
9、继承
ES6之前并没有提供extends继承。可以通过构造函数+原型对象模拟实现继承,被称为组合继承
9.1 call()
调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg, arg1, arg2, ...)
// thisArg:当前调用函数this的指向对象
// arg1,agr2:传递的其他参数
先看如下代码
<script>
function fn() {
console.log(this)
}
var o = {name: "zyzhang"}
// fn()
fn.call()
// 此时这个普通函数中的this指向的时Window这个对象
</script>
此时如果我们需要改变这个函数的this指向就可以这个做:
fn.call(o) // 此时函数中的this指向o这个对象
9.2 借用构造函数继承父类属性
如下代码子类就继承了父类的属性:
// 借用父构造函数继承属性
// 1 父构造函数
function Father(uname,age) {
// this指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 子构造函数
function Son(uname,age, score) {
// this 指向子构造函数的对象实例
Father.call(this,uname,age)
this.score = score
}
var son = new Son("zyzang",18,100)
console.log(son)
9.3 借用构造函数继承父类的方法
// 1 父构造函数
function Father(uname,age) {
this.uname = uname;
this.age = age;
}
// 2 子构造函数
function Son(uname,age, score) {
Father.call(this,uname,age)
this.score = score
}
// Son.prototype = Father.prototype 这样赋值会有问题,如果修改子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father()
// 如果利用对象形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son;
// 子构造函数专门的方法
Son.prototype.exam = function() {
console.log("考试")
}
var son = new Son("zyzang",18,100)
console.log(son)