JavaScript 05 作用域、this、使用工厂方法创建对象、构造函数、原型对象prototype、toString、垃圾回收、数组Array
作用域
作用域指一个变量的作用范围;
js中共有两种作用域:
- 全局作用域
- 函数作用域
全局作用域
直接编写在script标签中的js代码都在全局作用域;
全局作用域在页面打开时创建,页面关闭时销毁;
在全局作用域有一个全局对象window,window代表的是一个浏览器的窗口,由浏览器创建,可以直接使用;
变量的声明提前
创建的变量都会作为window对象的属性保存;
使用var关键字声明过的变量,会在所有的代码执行前被声明,但是如果声明时不使用var关键字,则变量不会被声明提前;
使用var
全局作用域中的全局变量,在页面的任意部分都可以访问到;
函数的声明提前
创建的函数都会作为window对象的方法保存;
使用函数声明形式创建的函数function函数(){},会在所有的代码执行前就被创建,所以可以在函数声明前来调用函数(优先级别高);
使用函数表达式创建的函数,不能被声明提前,所以不能在声明前调用;
函数作用域
调用函数的创建函数作用域,函数执行完毕以后,函数作用域销毁;
每调用一次函数,就会创建一个新的函数作用域,他们之间是相互独立的;
在函数中,可以访问到全局作用域的变量,在全局作用域中无法访问函数作用域的变量;
在函数中操作一个变量时,会先在自身作用域中寻找,如果找到则直接使用,如果没有则向上一级作用域寻找,直到找到函数作用域,如果函数中依然没有找到则会报错RefereceErorror;
在函数中要访问全局变量可以直接使用window对象;
函数作用域中声明提前的特性
使用var关键字声明的变量,会在函数中所有的代码执行之前被声明;函数声明也会在函数中的所有的代码执行之前执行;
在函数中不使用var声明的变量都会成为全局变量;
定义形参就相当于在函数作用中声明了变量;
debug调试
this
解析器在调用函数时每次都会向函数内部传递进一个隐含的参数,这个参数就是this,this指向的是一个对象,这个对象就称为函数执行的上下文对象;
根据函数的调用方式this会指向不同的对象:
-
以函数的形式调用时,this永远都是window;
-
以方法的形式调用时,this就是调用方法的对象;
使用工厂方法创建对象
通过该方法可以大批量的创建对象;
适用的构造函数都是Object,所以创造的对象都是Object这个类型,就导致无法区分出多种不同类型的对象;
步骤:
- 创建一个新的对象;
- 向对象中添加属性;
- 将新的对象返回;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function fun(name, age, gender){
//创建一个新的对象
var nihao = new Object();
//向对象中添加属性
nihao.name = name;
nihao.age = age;
nihao.gender = gender;
nihao.sayName = function(){
alert(this.name)
}
//将新的对象返回
return nihao;
}
var nihao2 = fun("红儿", 18, "女");
var nihao3 = fun("食神", 28, "男");
var nihao4 = fun("紫儿", 18, "女");
console.log(nihao2);
console.log(nihao3);
console.log(nihao4);
</script>
</head>
<body>
</body>
</html>
构造函数
创建XXX一类对象
专门用来创建XXX对象;
构造函数就是一个普通的函数创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写;
构造函数和普通函数的区别就是调用方式的不同,普通函数直接调用,而构造函数需要使用new关键字来调用;
使用同一个构造函数创建的对象称为一类对象,即为该类的实例;
使用instanceof可以检查一个对象是否是一个类的实例,语法:对象 instanceof 构造函数
,如果是则返回true,否则返回false;
所有的对象都是object的后代,所以任何和object做instanceof监察室都会返回true;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//创建一个新的对象
var nihao = new Object();
//向对象中添加属性
nihao.name = name;
nihao.age = age;
nihao.gender = gender;
nihao.sayName = function(){
alert(this.name)
}
//将新的对象返回
return nihao;
}
function Shenxian(){
}
var shenxian1 = new Shenxian();
var per = new Person("红儿", 18, "女");
var per2 = new Person("食神", 28, "男");
var per3 = new Person("紫儿", 18, "女");
console.log(per);
console.log(per2);
console.log(per3);
console.log(shenxian1);
console.log(per instanceof Object);
</script>
</head>
<body>
</body>
</html>
执行流程
- 立即创建一个新的对象;
- 将新建的对象设置为函数中的this,构造函数可用this来引用新建的对象;
- 逐行执行函数中的代码;
- 将新建的对象作为返回值返回;
this的情况
- 当以函数的形式调用时,this是window;
- 当以方法的形式调用时,谁调用方法this就是谁;
- 当以构造函数的形式调用时,this就是新创建的那个对象;
修改
在创建某类对象的构造函数中,为每个对象添加某个方法,由于这个方法创建在这个构造函数中,
则构造函数每执行一次就会创建一个新的这个方法,也就是说所有实例的这个方法都是唯一的,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = function(){
alert("我是" + this.name)
}
}
var per = new Person("红儿", 18, "女");
var per2 = new Person("红儿", 18, "女");
console.log(per.sayName == per2.sayName)
</script>
</head>
<body>
</body>
</html>
这样就导致了构造函数执行一次就会创建一个新的这个方法,完全没有必要,所以完全可以使所有对象都共享一个方法——即 将这个方法在全局作用域中定义;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
function fangfa(){
alert("我是" + this.name)
}
var per = new Person("红儿", 18, "女");
var per2 = new Person("红儿", 18, "女");
console.log(per.sayName == per2.sayName);
</script>
</head>
<body>
</body>
</html>
污染了全局变量的命名空间,而且定义在全局作用域中很不安全;
原型对象prototype
所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象;
当函数作为普通函数调用时prototype就没有任何作用;
当函数以构造函数调用时,它所创建的对象中都会有一个隐函属性,指向该构造函数的原型对象,可以通过__proto__来访问该属性;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
function fangfa(){
alert("我是" + this.name)
}
var per = new Person("红儿", 18, "女");
var per2 = new Person("红儿", 18, "女");
console.log(per.__proto__ == Person.prototype);
</script>
</head>
<body>
</body>
</html>
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,可以将对象中共有的内容统一设置到原型对象中;
当访问对象的一个属性或方法时会先在对象自身中寻找,如果有则直接使用,没有则会去原型对象中寻找如果找到则直接使用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
function fangfa(){
alert("我是" + this.name)
}
Person.prototype.gongzuo = "法师";
var per = new Person("红儿", 18, "女");
per.gongzuo = "辅助";
var per2 = new Person("红儿", 18, "女");
console.log(per.gongzuo);
</script>
</head>
<body>
</body>
</html>
向原型中添加原型
prototype.添加属性名 = 属性值;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
function fangfa(){
alert("我是" + this.name)
}
Person.prototype.gongzuo = "法师";
var per = new Person("红儿", 18, "女");
console.log(per.gongzuo);
</script>
</head>
<body>
</body>
</html>
prototype.添加方法名 = function(){};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
function fangfa(){
alert("我是" + this.name)
}
// Person.prototype.gongzuo = "法师";
Person.prototype.nihao = function(){
alert("你好");
}
var per = new Person("红儿", 18, "女");
// console.log(per.gongzuo);
per.nihao();
</script>
</head>
<body>
</body>
</html>
以后创建构造函数时,可以将这些对象共有的属性和方法统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
// this.sayName = fangfa;
}
//将sayName方法在全局作用域中定义
// function fangfa(){
// alert("我是" + this.name)
// }
// Person.prototype.gongzuo = "法师";
Person.prototype.sayName = function(){
alert("我是" + this.name)
}
var per = new Person("红儿", 18, "女");
// console.log(per.gongzuo);
per.sayName();
</script>
</head>
<body>
</body>
</html>
使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有也会返回true;
可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回true;
原型对象也是对象,所以他也有对象;
当使用一个对象的属性或方法时,会先在自身中寻找,自身中如果有则直接使用,如果没有则去原型对象中寻找,如果原型对象中有则直接使用,如果没有则去原型对象的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object对象的原型中依然没有找到,则返回Undefined;
toString()
当直接在页面中打印一个对象时,事件上是输出对象的toString()方法的返回值,如果希望在输出对象时不输出[object object],可以为对象中添加一个toString()方法;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
}
var per = new Person("红儿", 18, "女");
var jieguog = per.toString();
console.log("结果:" + jieguog);
console.log(per.__proto__.__proto__.hasOwnProperty("toString"));
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
}
var per = new Person("红儿", 18, "女");
per.toString = function(){
return "中国你好";
}
var jieguog = per.toString();
console.log("结果:" + jieguog);
console.log(per.__proto__.__proto__.hasOwnProperty("toString"));
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender){
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
}
var per = new Person("红儿", 18, "女");
per.toString = function(){
return "姓名:"+this.name + ",年龄:" + this.age +",性别:" + this.gender;
}
var jieguog = per.toString();
console.log("结果:" + jieguog);
console.log(per.__proto__.__proto__.hasOwnProperty("toString"));
</script>
</head>
<body>
</body>
</html>
修改原型中的toString()
某类实例的构造函数名.prototype.toString = function(){
return XXX;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
function Person(name, age, gender) {
//向对象中添加属性
this.name = name;
this.age = age;
this.gender = gender;
}
var per = new Person("红儿", 18, "女");
// per.toString = function () {
// return "姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.gender;
// }
Person.prototype.toString = function () {
return "姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.gender;
};
var jieguog = per.toString();
console.log("结果:" + jieguog);
console.log(per.__proto__.__proto__.hasOwnProperty("toString"));
</script>
</head>
<body>
</body>
</html>
垃圾回收(GC)
垃圾积攒过多以后,会导致程序运行的速度过慢,因此需要一个垃圾回收机制来处理程序运行过程中产生的垃圾;
当一个对象没有任何的变量或属性对他进行引用,此时我们将永远无法操作对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,这种垃圾必须进行清理;
在js中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,不需要也不能进行垃圾回收的操作;
我们需要做的只是要将不再使用的对象设置为null即可;
数组Array
数组也是对象;
数组和普通对象功能类似,也是用来存储一些值的;
不同的是普通对象是使用字符串来作为属性名的,而数组是使用数字来作为索引操作元素;
索引:从0开始的整数就是索引;
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据;
创建数组对象
创建一个数组中只有一个元素10数组名=[10];
创建一个长度为10的数组数组名 = new Array(10);
数组中的元素可以是任意数据类型,也可以是对象,也可以是函数(调用时为[]();
),也可以是数组;
构造函数
语法:var 数组名 = new Array();
使用构造函数创建函数时,也可以同时添加元素,将要添加的元素作为参数传递,元素之间使用 , 隔开;
使用typeof检查一个数组中会返回object;
数组字面量
语法:var 数组名 = [];
使用字面量创建数组时,可以在创建时就指定数组中的元素;
向数组中添加元素
语法:数组[索引] = 值
读取数据中的元素
语法:数组[索引]
如果读取不存在的索引,不会报错而会返回undefined;
获取数组的长度(元素的个数)
语法:数组.length;
对于连续的数组,使用length可以获取到数组的长度(元素的个数);
对于不连续的数组,注意不要创建非连续数组;
修改length
如果修改的length大于原长度,则多出部分会空出来;
如果修改的length小于原长度,则多出的元素会被删掉;
向数组的最后一个位置添加元素
数组名[数组名.length] = 值;
数组的方法
push()
该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度;
pop()
该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回;
unshift()
向数组开头添加一个或多个元素,并返回新的数组长度;
向前边插入元素以后,其他的元素索引会依次调整;
shift()
可以删除数组的第一个元素,并将被删除的元素作为返回值返回;
slice()
可以用来从数组中提取指定元素;
该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回;
索引可以传递一个负值,如果传递一个负值则从后往前计算;
参数
- 截取开始位置的索引;(包含开始索引)
- 截取结束位置的索引;(不包含结束索引),该参数可以省略不写,此时会截取从开始索引往后的所有元素;
splice()
可以用于删除数组中的指定元素;
使用splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回;
concat()
可以连接两个或多个数组,将新的数组返回;
该方法不会对原数组产生影响;
join()
该方法可以将数组转换为一个字符串;
该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回;
该方法中可以指定一个字符串作为参数,这个字符串将会作为数组中元素的连接符,
如果不指定连接符则默认使用 , 作为连接符;
reverse()
该方法用来反转数组(前边的去后边,后边的去前边);
该方法会直接修改原数组;
sort()
可以用来对对数组中的元素进行排序;
默认按照Unicode编码进行排序;
即使对于纯数字的数组也会按照Unicode编码进行排序(所以对数字进行排序时极可能得到错误的答案);
该方法会影响原数组;
可以自己指定排序的规则
可在sort()中添加一个回调函数来指定排序规则,回调函数中需要两个形参;
浏览器将会分别使用数组中的元素作为实参去调用回调函数,使用哪个元素调用不确定,但是肯定的是在数组中a一定在b前边;
浏览器会根据回调函数的返回值来决定元素的顺序:
-如果返回一个大于0的值,则元素会交换位置;
-如果返回一个小于0的值,则元素位置不变;
-如果返回一个0,则认为两个元素相等,也不会交换位置;
-return a-b;——>升序
-return b-a;——>降序
数组的遍历
即将数组中所有的元素都取出来;
forEach()
一般都使用for循环遍历数组,js中提供了了一个方法来遍历数组;
forEach()方法支持IE8以上的浏览器;
forEach()方法需要一个函数作为参数,这种函数由我们创建但不由我们调用(回调函数);
数组中有几个元素函数就会执行几次,每次执行时浏览器会将遍历到的元素以实参的形式传递进来,我们可以定义形参来读取这些内容;
浏览器会在回调函数中传递三个参数:
第一个参数就是正在遍历的元素,
第二个参数就是在遍历的元素的索引,
第三个参数就是正在遍历的数组;
语法:
数组名.forEach(function(第一个参数,第二个参数,第三个参数){
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript</title>
<script>
var shuzu = [1,5,2,8,7,6];
shuzu.forEach(function(yuansu, suoyin, shuzuming){
console.log("元素:" + yuansu + ",索引:" + suoyin + ",数组:" + shuzuming);
});
</script>
</head>
<body>
</body>
</html>