函数声明和函数表达式的区别
- 函数声明如果是放在if-else语句中,在IE8浏览器中可能会出现问题,因为IE8浏览器中在预解析的时候会把函数声明提前,如果有同名的函数,那么后面的函数声明会把前面的函数声明覆盖掉
- 以后可以用函数表达式的尽量用函数表达式
函数的调用
- 普通函数====》直接调用
- 构造函数====》通过new的方式调用
- 对象方法====》通过对象调用
函数中的this指向问题
- 普通函数中的this=====》window(页面中的所有内容都是window的)
- 对象.方法中的this======》当前的实例对象
- 定时器方法中的this=====》window(window.setInterval())
- 构造函数中的this======》实例对象
- 原型对象方法中的this======》实例对象
严格模式
- 在需要使用严格模式的代码上面加上 “use strict”; 那么这个语句后面的代码就必须遵循严格模式的规则(比如方法一定要通过对象调用,对象不能省略,每一个语句都要以分号结尾…),否则会报错
函数也是对象
- 因为函数中既有prototype,也有__ proto__,对象不一定是函数,但是函数一定是对象
- 所有函数实际上都是由Function的构造函数创建出来的实例对象(即函数的__ proto__指向的是Funtion构造函数的原型对象)
数组中可以存储任何类型的数据,也可以存函数
apply和call方法
-
apply和call都可以调用函数并且 改变函数中this的指向
-
apply的使用语法
- 函数名字.apply(对象,[参数1,参数2,参数3,…]);====》传参数的方式是数组
- 方法名字.apply(对象,[参数1,参数2,参数3,…]);
-
call的使用语法
- 函数名字.call(对象,参数1,参数2,参数3,…);
- 方法名字.call(对象,参数1,参数2,参数3,…);
-
两个方法的作用都是改变this的指向,第一个参数就是函数中this,当传进的是null时函数中的this就是默认的window
-
只要是想使用别的对象的方法,并希望这个方法是当前对象的,那么就可以使用apply或者call方法改变被调用的方法中this的指向
-
函数也是对象,对象能够调用apply和call方法说明对象中有这两个方法,要么是在对象实例中存在,要么是在对象所在的构造函数的原型对象中存在,事实上所有的函数都是Function构造函数的实例对象,apply和call方法就是Function.prototype中的方法
function f1(x, y) {
console.log((x + y) + this);
}
f1(10, 20);//普通函数中的this是window=====》这是普通函数调用的一般方式
console.log("======================");
f1.apply();//======>NaN[object Window]
f1.call();//======>NaN[object Window]
/**
* apply和call方法也可以实现对函数的调用
* 当不传参数时,f1函数中的this也是window
*/
console.log("======================");
f1.apply(null, [10, 20]);//======>30[object Window]
f1.call(null, 10, 20);//======>30[object Window]
/**
* apply方法有两个参数,第一个是一个对象,第二个是一个数组(参数数组)
* call方法的第一个参数也是一个对象,之后是个数不定的参数列表
* 当apply和call方法的第一个参数是null时,f1函数中的this还是window
* 第一个对象之后的参数是调用f1时传进到f1中的参数====》apply中使用数组,call中不是数组
*/
console.log("======================");
function f2(x, y) {
console.log((x + y) + this);
return "10000";
}
var result1 = f2.apply(null, [10, 20]);
var result2 = f2.call(null, 10, 20);
console.log(result1);//=======>10000
console.log(result2);//=======>10000
/**
* 当函数有返回值时,使用apply和call方法调用函数的返回值就是被调用函数的返回值
*/
console.log("======================");
function f3(x, y) {
console.log("" + (x + y) + this.sex);
}
window.f3(10, 20);//======>30undefined
obj = {
age: 10,
sex: "男"
};
f3.apply(obj, [10, 20]);//======>30男
f3.call(obj, 10, 20);//=====>30男
/**
* 使用apply和call方法调用函数可以改变函数中this的指向
* 将obj对象作为第一个参数,那么被调用的f3方法就不再属于window了,而是属于obj了,f3方法中的this指向的就是obj对象了
*/
console.log("======================");
//apply和call方法实现改变函数中this的指向的作用
function Person(sex) {
this.sex=sex;
}
//通过原型添加方法
Person.prototype.sayHi=function () {
console.log("您好哇:"+this.sex);
};
var per=new Person("男");
per.sayHi();//=======>您好哇:男
function Student(sex) {
this.sex=sex;
}
var stu=new Student("女");
per.sayHi.apply(stu);//因为sayHi函数本来就没有参数,所以只需要把想指向的对象传进去即可=========》您好哇:女====》apply方法改变了sayHi方法中this的指向
per.sayHi.call(stu);//======>您好哇:女
bind方法
- bind方法用来把函数复制一份,返回值复制之后的函数
- bind的使用语法
- 函数名字.bind(对象,参数1,参数2,…);
- 方法名字.bind(对象,参数1,参数2,…);
- bind方法的第一个参数就是复制的函数中的this,当传进的是null时this就是默认的window
function f1(x, y) {
console.log((x + y) + "========>" + this);
}
var ff = f1.bind(null, 10, 20);
ff();//30========>[object Window]
/**
* bind方法指的是把调用bind方法的函数复制一份,同时把参数传到复制的函数中去,返回值是一个函数
* 第一个参数是对象(函数中的this,默认是window),之后的参数是要传到函数中的参数列表
*/
var ff = f1.bind(null);
ff(10, 20);//30========>[object Window]
/**
* 参数可以在调用bind复制函数的时候传进去,也可以在复制完之后调用的时候传递
*/
function Student() {
}
Student.prototype.study = function () {
console.log("今天也是开心地学习");
};
var stu = new Student();
var ff = f1.bind(stu);
ff(20, 30);//50========>[object Object],说明复制的时候就把f1函数中的this指向改变了成为stu对象
/**
* apply和call方法是在函数调用的时候改变this的指向
* bind方法是在复制的时候改变函数中this的指向
*/
function ShowRandom() {
this.number = Math.random() * 10 + 1;
}
ShowRandom.prototype.show1 = function () {
// setInterval(function () {
//
// },1000);
window.setInterval(this.show2.bind(this), 1000);//==================》理解这几个this!!!!
};
ShowRandom.prototype.show2 = function () {
console.log(this.number);
};
var sr=new ShowRandom();
sr.show1();
函数中的几个成员
- name属性====》这个属性是只读的
- arguments属性====》f1.arguments.length指的是函数调用时实参的个数
- length属性====》函数定义时形参的个数
- caller属性====》函数的调用者,如f1函数是在f2函数调用的,那么f1.caller就是f2
函数是可以作为参数使用的
函数也是可以作为返回值使用的
作用域、作用域链和预解析
- 变量分局部变量(在函数内使用var声明的变量)和全局变量,作用域就是变量的使用范围
- 作用域分为全局作用域和局部作用域,但是JS中没有块级作用域的概念
- 作用域链:调用一个变量时,从里向外层层搜索,每一层的级别从里向外递减,搜索到0级作用域时如果还没找到变量,那么结果就是报错
- 预解析:指的是浏览器在解析代码之前,会先把变量声明和函数声明提前(提升)到当前作用域的最上面(注意:声明会提升,但是赋值不会提升)