javascripts_this指向&&函数进阶&&继承&&递归

javascripts知识点梳理5

this指向

指向window

1
2
3
4
function fn (){
console.log(this) // window
}
fn();
1
2
3
setInterval(function(){
console.log(this)
})

指向实例对象 obj

1
2
3
4
5
function fn (name , age){
...
console.log(this)
}
var obj = new fn ('zs' , 21);

this指向对象本身

1
2
3
4
5
6
7
var obj = {
name:'zs',
age:12,
fn function() {
console.log(this)}
}
obj.fn()

事件源

1
2
3
btn.onclick = function() {
//this指向事件源
}

改变this指向

1
fn.call(obj , 参数1 , 参数2)   对函数调用,同时修改调用时this指向obj
1
fn.apply(obj , [参数1 , 参数2])   对函数调用,同时修改调用时this指向
1
2
fn.bind(obj, 参数)  永久改变this指向  返回函数副本  不会调用执行
bind后续参数调用时进行实参传入,表示被绑定实参后续参数
1
2
3
4
5
6
7
fun.call({name : 'jack'});
fun.call({name : 'jack'});

var fun2 = fun.bind({name : 'rose'});
fun2();
fun2();
fun2();

函数进阶

回调函数

回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。

曾经用过的回调函数
当一个固定函数中的代码不确定时使用

1
2
3
4
5
6
$('#box').fadeIn(1000, function () {});
setInterval(function () {}, 100);
btn.on('click', function () {});
arr.sort(function (a, b) {
return a - b;
});

闭包

函数内部变量能被外部使用
对私有变量操作的函数,称之为闭包
原理:不对原函数进行二次调用,而是对函数返回结果进行多次调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function fun() {
var num = 100;
var obj = {
getValue: function() {
console.log(num);
},
setValue: function() {
num++;
},
setValue2: function() {
num--
}
};
return obj;
// 通过对象可以一次返回多个闭包函数
}
var result = fun();
result.getValue();
result.setValue();
result.getValue();

通过对象可以一次返回多个闭包函数

函数使用方式

函数作为参数

函数作为返回值

自执行函数

可以在自执行函数的形参与实参中写入window , document
可以提高性能

1
2
3
4
5
6
7
(function(window, document) {
console.log(window.num);
console.log(window.str);

document.getElementById();

})(window, document);

函数方法

函数声明方式

function fun () {}

    // 函数声明语句在预解析阶段会进行函数提升,将函数体提升到当前作用域顶端
    // 下面的代码在不同的浏览器中具有不同的展现效果
    /*if (true) {
        function fun () {
            console.log('这是if中的函数体');
        }
    } else {
        function fun () {
            console.log('这是else中的函数体');
        }
    }
    fun();*/

// 由于函数声明语句会存在一些问题,所以通常推荐使用函数表达式进行函数声明。
// 但是如果执意希望使用函数声明语句:遵循以下规则即可
// 1 不要在if中书写函数声明语句,使用函数表达式
// 2 建议在函数声明后再进行调用

    /*var fun;
    if (true) {
        fun = function () {
            console.log('这是if中的函数体');
        }
    } else {
        fun = function () {
            console.log('这是else中的函数体');
        }
    }
    fun();*/

函数表达式方式

var fun = function () {};

构造函数方式

var fun = new Function(‘x’, ‘y’, ‘console.log(x, y);’);

函数的属性

​ fn.name 函数名
fn.length 形参的长度
arguments.callee 可以得到当前函数
arguments 一个函数的参数和其他属性
fn.arguments 用于访问实参,作用和arguments对象相同
fn.caller 用于获取函数的调用位置

1
2
3
4
5
6
7
8
9
10
function showcallee(a,b) {
var c = a + b;
console.log(showcallee.name)
console.log(showcallee.length)
console.log(arguments.callee)
console.log(arguments)
console.log(showcallee.arguments)
return c;
}
showcallee(1,2);
1
2
3
4
5
6
7
function outer(){
inner();
}
function inner(){
console.log(arguments.callee.caller);
}
outer();

函数的方法

call() 用于对函数进行调用,同时可以修改调用时的this指向

apply() 用于对函数进行调用,同时可以修改调用时的this指向

bind()方法不会立刻调用函数,而是在调用bind后返回一个当前函数的副本。
参数1:可以绑定函数副本中的this指向
与call和apply的第二个区别:
call和apply只会在调用时修改一次this的指向,bind会将返回的函数副本中的this永久的设置为指定对象。

立即执行函数

1
2
3
4
(function (window, document) { 
console.log(window.num);
document.getElementById();
})(window, document);

继承

单一继承

函数call方法

​ 函数执行两种方法:1,调用 2,事件驱动
fun.call(); 与fun()完全一致
当传入第一个参数(对象)后,表示设置当前函数内的this指向当前对象,后面按顺序传入实参

函数执行两种方法:1,调用 2,事件驱动

fun.call(); 与fun()完全一致

当传入第一个参数(对象)后,表示设置当前函数内的this指向当前对象,后面按顺序传入实参

实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fun.call()不传参数==fun()
fun.call(obj); 传入参数1后,表示设置当前函数内的this指向当前对象。
fun.call(obj, 100, 200) 传入后续参数,用于表示实参。


练习的案例
<script>
function people(name, age) {
this.name = name;
this.age = age;
// this.sayHi = function() {
// console.log(this.name + '今年' + this.age + '岁了');

// }
}
people.prototype.sayHi = function() {
console.log(this.name + '今年' + this.age + '岁了');

}

function student(name1, age1, banji) {

// 利用call的第一个参数改变people的指向
people.call(this, name1, age1);
this.banji = banji;
}
student.prototype.sayHi1 = function() {
console.log(this.name + '今年' + this.age + '岁了');

}
var obj = new student('jack', 28, '2班');

console.log(obj);
obj.sayHi1();
obj.sayHi();
</script>

借用构造函数继承

这种方式可以继承属性,并且可以传入参数,但无法继承方法

在Student中的this指向jack jack也就是student这个函数,
如果将jack传入到call中,意味着修改People函数中的this为jack
这时两个构造函数的this都指向jack

实例
1
2
3
4
5
6
7
8
9
10
11
function People(name, age) {
this.name = name;
this.age = age;
}
function Student(name1, age1, banJi) {
People.call(this, name1, age1);
this.class = banJi;
}
var jack = new Student('jack', 21, '2班');
console.log(jack);
jack.sayHi();

原型继承

People的实例对象拥有People的方法,令Student的原型对象等于一个People的实例对象,之后便可调用People原型对象中的方法.

继承顺序:原函数内>附加函数内>原函数外>附加函数外
单独设置的功能需要在继承操作后设置

由于使用了覆盖方式设置原型constructor被改为People
需要补全constructor属性
Student.prototype.constructor = Student;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function People(name, age) {
this.name = name;
this.age = age;
}
People.prototype.sayHi = function() {
console.log('你好,我叫' + this.name);
};
function Student(name1, age1, banJi) {
People.call(this, name1, age1);
this.class = banJi;
}
Student.prototype = new People();
Student.prototype.constructor = Student;
var jack = new Student('jack', 21, '2班');
console.log(jack);
jack.sayHi();

组合继承

将两种单一继承组合,兼顾两者优点

构造函数与原型对象与实例对象的关系

实例对象:通过构造函数创建的,具有一个属性__proto__,指向构造函数的原型对象。
构造函数:具有一个属性prototype,指向构造函数的原型对象。
原型对象:具有一个属性constructor,指向构造函数。

实例对象

   function Person(personName) { // 括号内为参数,可以设置多个参数来定制对象。
        this.name = personName; // 将传入的personName值赋予name属性
        this.info = 'I am called' + this.name; //设置info属性
        this.infoShow = function () {   // 利用匿名函数添加方法
            alert(this.info);
        }
    }
1
2
var person1 = new Person('Adam');
var person2 = new Person('Eve'); //利用构造函数实例化了两个对象,并根据传入的参数的不同,定制了两个不同的对象

person1 person2 都是 Person 构造函数 的实例对象

原型对象

任何一个JavaScript对象(一般有对花括号)都有一个原型对象,这个原型对象是JavaScript语言内置的,JavaScript对象可以使用原型对象的属性和方法。

实例对象:通过构造函数创建的,具有一个属性__proto__,指向构造函数的原型对象。
构造函数:具有一个属性prototype,指向构造函数的原型对象。
原型对象:具有一个属性constructor,指向构造函数。

原型链

原型链的作用:
// 1 用于描述实例对象与原型对象之间关系的一种方式。
// 2 用于描述对象属性的查找方式(沿原型链向上查找)。
// 原型链的终点:Object.prototype

用于描述实例对象与原型对象之间关系的一种方式
也用于描述对象属性的查找方式(沿原型链向上查找)

递归

可以利用递归进行深拷贝

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var obj = {
name : 'jack',
age : 18,
aiHao : {
qiMa : '我喜欢骑大白马',
swim : '我喜欢自由泳',
daQiu : {
lanQiu : '因为喜欢看NBA,所以也喜欢打篮球',
zuQiu : '我喜欢c罗,所以也喜欢踢足球'
}
},
arr : [1, 2, 3]
};
// 如果希望对一个复杂数据类型进行复制,可以使用遍历拷贝的方式
// 但是如果对象的属性值也是对象,就不能只执行一次拷贝操作了,需要对这个属性进行遍历拷贝
var obj2 = {};

/*for (var k in obj) {
obj2[k] = obj[k];
}
obj.name = 'rose';
obj.aiHao.qiMa= '我喜欢骑大黑马';
console.log(obj, obj2);*/

// 对功能进行完善:
function deepCopy (obj, obj2) {
for (var k in obj) {
// 检测对象的某个属性是基本数据类型还是复杂数据类型
if (typeof obj[k] !== 'object' || obj[k] === null) {
// 说明是基本数据类型
obj2[k] = obj[k];
} else {
// 说明是复杂数据类型
// 由于obj2[k]是一个新设置的属性,所以默认为undefined,如果需要进行拷贝,应当设置为对象
// 由于复杂数据类型除了对象以外还有数组形式,还需要考虑obj[k]的具体类型
obj2[k] = obj[k].constructor === Array ? [] : {};
deepCopy(obj[k], obj2[k]);
}
}
}
// 表示将obj中的所有属性拷贝到obj2中
deepCopy(obj, obj2);
obj.aiHao.qiMa = '我喜欢骑大黑马';
console.log(obj, obj2);

// 通过以上案例我们发现,递归是可以不显示的设置结束条件的。

FB数列的计算


javascripts_this指向&&函数进阶&&继承&&递归
http://www.dwyblog.cn/2019/10/10/javascripts_this指向&&函数进阶&&继承&&递归/
作者
幸福来敲门
发布于
2019年10月10日
许可协议