JavaScript基础
本章节的作用域需要重点理解并掌握,万变不离其宗。
51. 立即执行函数
匿名函数外加括号表示一个整体,再在其后边加括号,表示立即执行函数,因没有用变量去保存,所以只执行一次
如果前边括号内有形参,则可以在后边括号传递实参
(function(a,b){
console.log(a);
console.log(b);
})(123,456);
52. 方法
对象的属性值可以是任何的数据类型,也可以是个函数
如果一个函数作为一个对象的属性保存,称这个函数为对象的方法
调用这个函数就是说调用对象方法
调用对象的方法和调用函数没有本质区别,只是名字不一样
使用for … in 语句来枚举对象中的所有属性
语法:
for(var 变量 in obj){语句…}
例如:for(var n in obj){console.log(n);}
var obj = new Object()
obj.name = "张三";
obj.age = 18;
obj.sayname = function(){ //对象的属性可是函数
console.log(obj.name);
}
obj.sayname();//调用对象的方法,而fun()称为调函数
for(var n in obj){
console.log(n);//输出属性
// console.log(obj.n);//输出属性值为undifined
console.log(obj[n]);//输出属性值
}
53. 全局作用域
作用域:指一个变量的作用范围
全局作用域:直接编写在script标签中的JS代码
在页面打开时创建,在页面关闭时销毁
全局对象window可以直接在页面中调用
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
创建的全局变量在页面所有位置都可以被调用
函数作用域(局部作用域)
变量的声明提前:
使用var关键字声明的变量会在所有代码执行之前被声明(但是不会被赋值)
函数的声明提前:
使用函数声明形式创建的函数:function 函数名( ){ },会在所有代码执行之前被创建,因此可以在函数声明前调用
利用函数表达式创建的函数,不会被声明提前,因此不能在声明前调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
console.log(window);//全局对象window由浏览器创建
var a = 10;
console.log(window.a);//创建的变量作为window对象的属性保存
function fun(){
console.log("我是第一个函数");
}
window.fun();//创建的函数作为window对象的方法保存
b = 1;
console.log(b);//声明变量时不使用var相当于window.b
console.log(c);//var声明的变量会在所有代码执行之前被声明(但不会被赋值)
var c = 2;//如果不使用var则会报错ReferenceError
fun1();//函数声明提前,所有代码执行前被创建
function fun1(){
console.log("我是第二个函数");
}
// fun2();//没有函数声明提前,会报错,但fun2变量已被声明
var fun2 = function(){
console.log("我是第三个函数");
};
// fun2(); //声明之后才能调用方法
</script>
</head>
<body>
</body>
</html>
54. 函数作用域
函数作用域(局部作用域):
调用函数时创建,函数执行完毕后,函数作用域被销毁
每调用一次就会创建一次函数作用域,它们之间相互独立
函数作用域中可以访问到全局作用域的变量
全局作用域中无法访问到局部作用域的变量
在函数作用域调用变量时先在自身作用域范围查找变量,否则上一级查找
想在函数作用域中访问全局变量可以使用window对象
函数作用域中也有使用var声明提前的特性
函数作用域中也有函数声明提前的特性
在函数中不使用var声明的变量都会成为全局变量,相当于使用window.变量
定义形参就相当于在函数作用域中声明了变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
var a = 10;
function fun(){
var a = 11;
var b = 20;
console.log(a);//先自身作用域中查找变量a,没有上一级查找
console.log(window.a)//使用window对象访问全局作用域的变量
}
fun();//函数作用域中可以访问到全局作用域的变量
// console.log(b);//全局作用域无法访问到函数作用域的变量
function fun1(){
console.log(a);//变量a声明提前,但值为undifined
var a = 30;
fun2();//函数声明提前
function fun2(){
console.log("我是函数");
}
}
fun1();
var c = 3;
function fun3(){
console.log(c);
c = 4;
}
fun3();//输出全局的c,为3
console.log(c);//在函数中不使用var声明的变量都会成为全局变量,输出4
var e = 5;
function fun4(e){
console.log(e);
}
fun4();//定义形参相当于声明了变量,但没有赋值,输出undifined,而不是5
</script>
</head>
<body>
</body>
</html>
【注意】在函数中不使用var声明的变量都会成为全局变量,相当于使用window.变量(在没有传参的情况下!!!)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
var a = 123;
function fun5(a){
console.log(a);
a = 456
console.log(a);
}
fun5(123);//a=123,456
console.log(a);//a=123
//在有传参情况下,函数里面有声明a并且传值为123,此时修改a为456
//修改的是函数作用域里的a,而全局作用域的a为123没改变
var b = 123;
function fun3(){
console.log(b);
b = 456;
console.log(b);
}
fun3();//a=123,456
console.log(b);//a=456
//没有传参情况下,函数里面b为全局变量,全局作用域输出b为修改后的
</script>
</head>
<body>
</body>
</html>
55. debug
完成练习:全局作用域和局部作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
var a = 123;
function fun1(){
console.log(a);
}
fun1();//a=123
var a = 123;
function fun2(){
console.log(a);
var a = 456;
}
fun2();//a=undifined
console.log(a); //a=123
var a = 123;
function fun3(){
console.log(a);
a = 456;
}
fun3();//a=123
console.log(a);//a=456
var a = 123;
function fun4(a){
console.log(a);
a = 456;
}
fun4();//a=undifined
console.log(a);//a=123
var a = 123;
function fun5(a){
console.log(a);
a = 456;
}
fun5(123);//a=123
console.log(a);//a=123
</script>
</head>
<body>
</body>
</html>
浏览器Debug测试:以54. 函数作用域中的注意代码为例
以Google Chrome浏览器:
- 打开网页,鼠标右键进入检查,点击上方source
-
在想要看的那一行鼠标左键点击,设置断点,然后刷新页面,点击右边local和global
此时代表,程序执行到断点处,右边可以看到函数作用域里a为123,全局作用域a为123 -
点击下一步,继续执行代码
可以看到光标颜色移动到了下一行,右边函数作用域里a修改为456,因为这个函数是有传参的,此时的a修改的是函数作用域里的a的值,虽然它没有使用var声明
56. this
解析器在调用函数每次都会想函数内部传递一个隐含的参数this
this指向一个函数执行的上下文对象
根据函数调用方式的不同,this会指向不同的对象
以函数的形式调用时,this永远是window
以方法的形式调用时,this就是调用方法的那个对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
function fun(){
console.log(this);
}
var obj={
name:"孙空悟",
sayName:fun
};
fun();//object window,以函数形式调用时,this为window
obj.sayName();//object obj,以方法的形式调用时,this为调用方法的对象
</script>
</head>
<body>
</body>
</html>
57. this补充
可以使用this来进行动态调用输出
var name = "全局";
function fun(){
console.log(this.name);
}
var obj1 = {
name:"张三",
sayName:fun
};
var obj2 = {
name:"李四",
sayName:fun
};
obj1.sayName();//当前为obj1调用,this为obj1
obj2.sayName();//当前为obj2调用,this为obj2
58. 工厂方法创建对象
通过传递属性参数,批量生成对象,更加简单避免重复工作
function createPerson(name,age,gender){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function(){
console.log(this.name);
};
return obj;
}
var obj1 = createPerson("张三",18,"男");
obj1.sayName();
var obj2 = createPerson("李四",19,"女");
obj2.sayName();
var obj3 = createPerson("王五",20,"男");
obj3.sayName();
59. 构造函数
使用工厂方法创建的对象,使用的构造函数都是object,导致无法区分多种不同类型的对象
构造函数就是一个普通的函数,不同的是构造函数习惯首字母大写
构造函数需要使用关键字new来调用
执行流程:
-
立刻创建一个新的对象
-
将新创建的对象设置为函数中this,可以使用this来引用新创建的对象
-
逐行执行函数中的代码
-
将新建的对象作为返回值返回(浏览器自动返回)
使用同一构造函数创建的对象,称为一类对象,也将一个构造函数称为一个类
通过构造函数创建的对象,称为该类的实例
使用instanceof可以检查一个对象是否是一个类的实例,是返回true,否则false
语法:
对象 instanceof 构造函数
this总结:
当以函数的形式调用时,this是window
当以方法的形式调用时,this是调用者
当以构造函数形式调用时,this是新创建的对象
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
console.log(this.name);
};
}
var per = new Person("张三",18,"男");//构造函数使用new进行调用
console.log(per);//Person类
function Dog(){
}
var dog = new Dog();
console.log(dog);//Dog类
console.log(per instanceof Person);//per为Person类的实例
console.log(dog instanceof Dog);//dog为Dog类的实例
console.log(dog instanceof Object);//所有对象都为Object类的实例
60. 构造函数的修改
在上一节,Person构造函数中,为每一个对象都添加了sayName方法
意味着构造函数每执行一次就会创建一个新的sayName方法,
即所有实例的sayName都是唯一的,这样就导致内存空间的浪费
因此可以把sayName方法在全局作用域中定义
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = fun;
}
function fun(){
console.log(this.name);
}
var per1 = new Person("张三",18,"男");
per1.sayName();
var per2 = new Person("李四",19,"男");
per2.sayName();
console.log(per1.sayName==per2.sayName);//不同对象相同的方法
下一章节将对原型对象、垃圾回收、数组进行归纳总结