继承和闭包
一、面向对象的三大特征
- 封装
- 继承
- 多态
二、什么是继承
继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
三、继承的三种方法
- 1.
call
方法
// 构造函数-Person
function Person(name, age) {
this.name = name;
this.age = age;
this.Say = function () {
console.log("吃东西")
}
}
// 构造函数-Student来继承Person
function Student(name,age){
Person.call(this,name,age)
}
const s1 = new Student('张三',24);
console.log(s1.name,s1.age);
s1.Say();
但是上述方法不能继承原型对象的属性和方法
- 拷贝继承
//人-构造函数
function Person(name, age) {
this.type = 'human'
this.name = name
this.age = age
this.say = function () {
console.log("说话>>");
}
}
Person.prototype = {
constructor: Person,
eat: function () {
console.log('吃东西');
}
}
//学生-构造函数
function Student(name, age) {
Person.call(this, name, age); // 借用构造函数继承成员
}
// 原型对象拷贝继承原型对象成员
for (const key in Person.prototype) {
Student.prototype[key] = Person.prototype[key];
}
var s1 = new Student('张三', 18)
console.log(s1.type, s1.name, s1.age) // => human 张三 18
s1.say();
s1.eat();
- 原型继承
function Person(name, age) {
this.type = 'human'
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
function Student(name, age) {
}
// 利用原型的特性实现继承
Student.prototype = new Person()
var s1 = Student('张三', 18)
console.log(s1.type) // => human
s1.sayName() // => hello 张三
四、函数的this指向问题
调用方式 | 非严格模式 | 备注 |
---|---|---|
普通函数调用 | window | 严格模式下是undefined |
构造函数调用 | 实例对象 | 原型方法中的this也是实例对象 |
对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
事件绑定方法 | 绑定事件的对象 | |
定时器函数 | window |
如下例所示:
//普通函数调用
const info = function(){
console.log('普通函数this :',this);
}
//info(); //window
// 构造函数调用
function Person(){
console.log('构造函数this ',this);//Person
this.name = '构造函数'
this.sayName = function(){
console.log('对象方法this ',this);//Person
}
}
// 原型对象
Person.prototype.eat = function(){
console.log('原型方式this :',this);//Person
}
Person.prototype.eat = () => {
console.log('原型方式this :',this);//箭头函数里没有this,该this指向为外层,即window
}
const p = new Person();
p.sayName();
p.eat();
// 事件绑定
const btn = document.getElementById('btn');
btn.onclick = function(){
console.log('事件绑定this ',this);//<button id="btn">按钮</button>
}
// 定时器
setTimeout(function(){
console.log('定时器this',this);
},1000)//window
五、call()和apply()的区别和作用
call()
和apply()
都可以改变this的指向
区别:它们的区别在于接受的参数方式不同
call()
:第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。在使用call()
方法时,传递给函数的参数必须逐个列举出来。apply()
:传递给函数的是参数数组
六、闭包
1.变量作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
另一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
var n=999;
}
alert(n); // error
2.闭包
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数,只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
上面代码的f2函数就是闭包,简单理解为:闭包就是能够读取其他函数内部变量的函数
闭包形成的条件:
- 函数嵌套
- 内部函数引用外部函数的局部变量
3.闭包的用途和特点
- 闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
//例题1
var inner;
function outer(){
var a=250;
inner=function(){
alert(a);//这个函数虽然在外面执行,但能够记忆住定义时的那个作用域,a是250
}
}
outer();
var a=300;
inner();//一个函数在执行的时候,找闭包里面的变量,不会理会当前作用域。
//例题2
function outer(x){
function inner(y){
console.log(x+y);
}
return inner;
}
var inn=outer(3);//数字3传入outer函数后,inner函数中x便会记住这个值
inner(5);//当inner函数再传入5的时候,只会对y赋值,所以最后弹出8
网友评论