####
js里面函数也是一个对象,js里面除了基本数据类型,其他的都是对象,
###
普通对象只能装属性,但是函数可以封装一些功能,
###
###
创建一个函数对象:函数定义
//第一种方法,使用function 关键字 声明一个函数,
function func1(a){console.log(a)}
// 被声明的函数不会直接执行。而是调用的时候才会执行,
// typeof func1; 这是一个function类型,
// 第二种方法使用函数表达式,使用变量接收函数,这个是一个匿名函数,
var func = function(a){console.log(a)}
//匿名函数可以使用一个变量接收函数,可以使用函数名()调用,
// 第三种方法,使用new关键字创建一个函数,但是开发中几乎不使用这种new的方式创建函数,
var func = new Function();
//通过名为 Function() 的内建 JavaScript 函数构造器来定义。
函数的参数
arguments,
// 函数在调用的时候,会传入两个隐含的参数,1个是函数的上下文this,第二个是封装了实参的对象,arguments,
// 这个arguments可以获取长度,也可以通过索引取值,
// 调用函数的时候,传入的实参,都会封装到这个argument里面,
function func1(a,b,c){console.log(arguments)}
func1(); // 这个是返回一个arguments对象,
function func1(a,b,c){console.log(arguments.length)}
func1(1,2,3); // 这个是返回3,参入实参的长度,
function func1(a,b,c){console.log(arguments[2])}
func1(“a”,”b”,”c”); // 这个是返回c,
// 调用的时候,如果传递的超出定义的,超出的不起作用,并不会导致程序报错,
//如果传递的参数少于定义的,未定义的就会显示undefined,也不会程序报错,
//这是js比较随便的地方,
this关键词
这个也是函数调用的时候,默认必传的参数之一,
如果是在函数内部找,这个this,就是window这个对象,
this是一个对象,是函数执行的上下文对象,
但是这个this,不是一直是window,而是和函数调用方式的不同,this指向不同的对象,
就是两种情况,
1,以函数调用的方式,this就是window这个对象
function func1(){console.log(this)}
func1(); 这个this打印出来就是Window对象,
2,如果是方法的调用,就是哪一个对象调用的,this就是这个对象,
var obj = {“name”:”tom”,”sayname”:function(){console.log(this)}}
obj.sayname(); // 这个this就是这个obj对象,
函数的调用
//第一种调用方法:
//上面定义的函数,都是使用函数名()调用的,
//第二种调用,立即执行函数
//先写两个括号,第一个括号内部定义,第二个括号内部传递具体的参数
(function (a,b) {
console.log(“立即执行函数”);
console.log(a+b)
})(11,222);
//第三种调用,call()和apply()
可以修改函数的this,
var person = {
firstName:”Steve”,
lastName: “Jobs”,
fullName: function() {
return this.firstName + ” ” + this.lastName;
}}
var person1 = {
firstName:”Bill”,
lastName: “Gates”,
}
person.fullName(); // 返回”Steve Jobs”
person.fullName.call(person1); // 返回”Bill Gates” 修改了this的值,
还可以传递参数:
var person = {
firstName:”Steve”,
lastName: “Jobs”,
fullName: function(city, country) {
return this.firstName + ” ” + this.lastName + “,” + city + “,” + country;
}
}
var person1 = {
firstName:”Bill”,
lastName: “Gates”,
}
person.fullName.call(person1, “Seattle”, “USA”); // 返回”Bill Gates,Seattle,USA”
###
call()和apply()区别:
这两个的用法很类似,但是传值的方式有差异,
apply需要传递一个数组的参数才可以,
person.fullName.apply(person1, [“Seattle”, “USA”]); // 返回”Bill Gates,Seattle,USA”
####
函数的作用域
函数的作用域
作用域只有两种,
1,全局作用域,
全局变量会在页面打开的时候生成,页面关闭后被销毁,
在全局作用域中,有一个全局对象,就是Window,这代表一个浏览器窗口,我们可以直接使用,
我们创建的全局变量,都会作为window的属性来保存的,所以比如,var name = “name”,使用window.name就可以拿出来,
创建的函数,都会作为window的方法来保存,这个很好理解,也很重要,
2,局部作用域,
调用函数时,创建作用域,函数执行完毕,以后,函数作用域销毁,
每次调用函数就会创建一个作用域,他们之间是独立的,
在函数里面定义的变量,在函数外是访问不到的,
在函数操作一个变量的时候,会优先在函数内部找,找不到会到上一级找,
如果想要在函数中,访问全局的变量,就是console.log(windows.a)
在函数中,不使用var生命的变量,都会成为全局变量,
如果函数中变量,没有使用var 定义变量,就会被认为是全局变量,
函数定义了形参,就相当于在函数中生命了变量,
局部变量:
在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。
全局变量:
在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。
变量生存周期:
JavaScript变量的生命期从它们被声明的时间开始。
局部变量会在函数运行以后被删除。
############### JS词法分析 ################
//js的词法分析 var age =18; function foo() { console.log(age); var age =22; console.log(age); } foo(); //结果是 undefined ,22 , //为什么? //因为函数执行的时候,会对函数内部进行词法分析 /* JavaScript中在调用函数的那一瞬间,会先进行词法分析。 词法分析的过程: 当函数调用的前一瞬间,会先形成一个激活对象:Avtive Object(AO),并会分析以下3个方面: 1:函数参数,如果有,则将此参数赋值给AO,且值为undefined。如果没有,则不做任何操作。 2:函数局部变量,如果AO上有同名的值,则不做任何操作。如果没有,则将此变量赋值给AO,并且值为undefined。 3:函数声明,如果AO上有,则会将AO上的对象覆盖。如果没有,则不做任何操作。 函数内部无论是使用参数还是使用局部变量都到AO上找。 * */
####
函数高级
// 使用构造函数创建对象
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person(“Bill”, “Gates”, 62, “blue”);
var myMother = new Person(“Steve”, “Jobs”, 56, “green”);
myFather // 返回 Person {firstName: “Bill”, lastName: “Gates”, age: 62, eyeColor: “blue”}
myMother // 返回 Person {firstName: “Steve”, lastName: “Jobs”, age: 56, eyeColor: “green”}
// 注意:Person构造函数的名字,最好是首字母大写,这是一个好习惯,
// 这个构造函数只是一个普通函数,但是你创建的时候使用new 就是一个构造函数了,会返回一个object,
// 这个this就是指的new的时候,返回的对象,谁new的,这个this就是谁,
// 给对象添加属性,和普通的对象是一样的方法
myFather.nationality = “English”;
// 给对象添加方法,和普通的对象是一样的方法
myFather.name = function () {
return this.firstName + ” ” + this.lastName;
};
// 给构造函数添加属性和方法,不能通过这种点的方式添加,必须要在构造函数内部添加,
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.nationality = “English”;
}
使用构造函数之后,就有一个问题就是原型对象的问题,
我们可以把共有的属性或者方法,放到原型对象中,这个就好像是一个父类的概念,
所有通过构造函数,创建的对象,都会继承原型对象中的方法和属性,
构造函数体现的封装的思想
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
var age = 18;
}
var myFather = new Person(“Bill”, “Gates”, 62, “blue”);
注意这个var age 实例化出来的对象,是不能访问的,也就是不能执行myFather.age,这个体现了封装的思想,
###
原型对象,原型链
每一个函数,创建的时候,都会往属性里面添加一个属性prototype,
这个属性是对应一个对象,这个对象就是我们所谓的原型对象,
所以构造函数本身也会有一个原型对象,
使用构造函数创建对象的时候,每一个对象都有一个隐含的属性都是指向这个构造函数的原型对象的,
可以通过__proto__来访问这个对象,‘
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person(“Bill”, “Gates”, 62, “blue”);
var myMother = new Person(“Steve”, “Jobs”, 56, “green”);
myFather.__proto__ == Person.prototype // 这个结果就是true
myMother.__proto__ == Person.prototype // 这个结果就是true
由于有这个性质,所以公共的方法和属性,可以放到原型对象里面,
// 往原型对象里面添加属性
Person.prototype.nationality = “English”;
// 往原型对象里面添加方法
Person.prototype.name = function() {
return this.firstName + ” ” + this.lastName;
};
所以这个时候,对象调用属性和方法的时候,优先在自己的方法属性里面找,找不到就会去原型对象里面找,
类似父类的概念,这个地方体现了继承的思想,
#####
解决了一个问题,
构造函数创建对象的方式,如果把方法放到构造函数,就会导致每次创建一个对象就会创建一个方法,创建1万个对象就是1万个方法,很浪费内存,
如果把这个方法写到全局变量里面,又会污染全局变量,如果别人使用了和你一样的变量名,就会导致你的方法被覆盖,也不可取,
而把这个公共的方法,放到原型对象里面,就完美的解决了这个问题,
###
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person(“Bill”, “Gates”, 62, “blue”);
Person.prototype.nationality = “English”;
“firstName” in myFather // 使用in,可以判断对象有没有这个属性,如果对象中没有,原型里有,也会是返回true,
myFather.hasOwnProperty(“nationality”) //使用hasOwnProperty,这个是判断这个是否是自己对象里面的属性,
注意:hasOwnProperty这个方法我本身没有定义这个方法,那是哪里来的?
往上找,是构造函数的原型对象里面的吗?也没有,
再往上找,构造函数的原型对象的原型对象,是object的原型对象,是这里面的方法,这个就是原型链的调用,
这样就到头了,
mc.__proto__.__proto__..hasOwnProperty(“hasOwnProperty”) // 这个就是true了,
toString方法揭秘
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person(“Bill”, “Gates”, 62, “blue”);
console.log(myFather.toString()) //打印出 [object Object]
myFather.__proto__.__proto__.hasOwnProperty(“toString”)
// 这个tostring方法是哪里定义的,就是原型对象的原型对象里面定义的,
我们可以把这个改掉,
myFather.toString = function(){console.log(“abc”)}
console.log(myFather.toString()) // 这样覆盖了toString原来的方法之后,这个就是打印abc了
###
函数提升
myFunction(5);
function myFunction(y) {
return y * y;
}
// 函数提升是 JavaScript 将声明移动到当前作用域顶端的默认行为。
####