垂直入坑JS
函数function
JavaScript中的函数也是一个对象,使用typeof检查一个函数对象时,会返回function
函数中可以封装一些功能(代码),在需要的时候可以执行这些功能(代码)
函数中可以保存一些代码在需要的时候调用
函数创建
//创建一个函数对象
//可以将要封装的代码以字符串的形式传递给构造函数
var fun=new Function("console.log('这是我的第一个函数');");
//封装到函数中的代码不会立即执行
//函数中的代码会在函数调用的时候执行//调用函数 语法:函数对象()
//当调用函数时,函数中封装的代码会按照顺序执行
fun();
- 使用 函数声明 来创建一个函数(比较常用)
语法:
function 函数名([形参1,形参2,...,形参N]) {
语句...
}
function fun2(){
console.log("这是我的第二个函数");
}
fun2();
- 使用 函数表达式 来创建一个函数(比较常用)
var 函数名 = function([形参1,形参2,...,形参N]) {
语句....
}
var fun3 = function() {
console.log("这是我的第三个函数");
}
fun3();
可以在函数的()中来指定一个或多个形参
多个形参之间使用,隔开
声明形参就相当于在函数内部声明了对应的变量但是并不赋值
在调用函数时,可以在()中指定实参
实参将会赋值给函数中对应的形参
函数调用
//定义一个用来求两个数之和的函数
function sum(a,b){
console.log(a+b);
}
sum(2,3);
注:
- 调用函数时解析器不会检查实参的类型
所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查 - 调用函数时解析器也不会检查实参的数量
多余实参不会被赋值
如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined
实参可以是任何值
参数过多封装到一个对象中
function Introduction(i){
console.log("我是"+i.name,"芳龄"+i.age,"性别"+i.gender);
}
var obj = {
name:"wdd",
age : 3,
gender:"男"
};
Introduction(obj);
实参可以是一个对象,也可以是一个函数
function fun(a){
console.log("a="+a);
}
fun(function(){alert("hello")});
函数返回值
可以使用 return 来设置函数的返回值,return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
function sum(a,b,c){
var d = a + b + c;
return d;
}
var result = sum(1,2,3);
console.log(result);
注:如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined,return后可以跟任意类型的值
返回值可以是任何数据类型
function fun3(){
//在函数内部再声明一个函数
function fun4(){
console.log("我是fun4");
}
//将fun4函数对象作为返回值返回
return fun4;
}
a = fun3();
console.log(a);
//fun3()();
立即执行函数
函数定义完,立即被调用
(function (){
alert("我是一个函数");
})
();
方法
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)
注:方法和函数只是名称上的区别,没有其它别的区别
var obj ={
name : "wdd",
age : 3,
gender : "男",
inner:function(){
console.log(name +"wawo")
}
}
obj.inner();
枚举对象中的属性
使用
for ... in
语句
对象有几个属性,循环体就会执行几次
每次执行时,会将对象中的一个属性的名字赋值给变量
语法:
for(var 变量 in 对象){
}
var obj = {
name :"wdd",
age : 3,
gender :"男",
};
for( var n in obj ){
console.log("属性名 "+n);
console.log("属性值 "+obj[n]);
}
全局作用域
作用域
- 作用域指一个变量的作用的范围
- 在JS中一共有两种作用域:
1. 全局作用域
直接编写在script标签中的JS代码,都在全局作用域全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window
它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
- 在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
全局作用域的变量都是全局变量
在页面的任意部分都可以访问的到
2. 函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的- 在函数作用域中可以访问到全局作用域的变量
- 在全局作用域中无法访问到函数作用域的变量
当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用
如果没有则向上一级作用域中寻找,直到找到全局作用域
如果全局作用域中依然没有找到,则会报错ReferenceError
在函数中,不适用var声明的变量都会成为全局变量
定义形参相当于在函数作用域中声明了变量
变量声明提前
- 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),
但是如果声明变量时不适用var关键字,则变量不会被声明提前函数声明提前
- 使用函数声明形式创建的函数function函数(){
}它会在所有的代码执行之前就被创建
使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
fun();
fun2();
function fun(){
console.log("我是函数fun");
}
var fun2 = function(){
console.log("我是函数fun2");
}
// fun();
// fun2();
this
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象
根据函数的调用方式的不同,this会指向不同的对象
- 以函数的形式调用时,this永远都是window
- 以方法的形式调用时,this就是调用方法的那个对象
function fun(){
console.log(this);
}
var obj ={
name:"wdd",
sayName : fun,
};
console.log(obj.sayName==fun);
//我们希望调用obj.sayName()时可以输出obj的名字而不是全局变量name的名字
obj.sayName();
// fun();
var name = "全局";
function fun(){
console.log(this.name)
}
var obj = {
name:"wdd",
sayName:fun,
};
var obj2 = {
name:"ddw",
sayName:fun,
};
obj.sayName();
obj2.sayName();
对象进阶
用工厂方法创建对象
function createPerson(name,age,gender){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function(){
alert(this.name);
}
return obj;
}
var obj2 = createPerson("wdd",3,"男");
console.log(obj2);
实现批量创建对象的功能
用构造函数创建对象
创建一个构造函数,专门用来创建
Person
对象的
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写
- 构造函数和普通函数的区别就是调用方式的不同
普通函数是直接调用,而构造函数需要使用new关键字来调用
构造函数的执行流程:
- 立刻创建一个新的对象
- 将新建的对象设置为函数中this
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
alert(this.name);
}
}
var per = new Person("wdd",3,"男");
var per2 = new Person("ddw",3,"男");
var per3 = new Person("dwd",3,"男");
console.log(per);
console.log(per2);
console.log(per3);
使用instanceof可以检查一个对象是否是一个类的实例
语法:
对象instanceof
构造函数
是则返回true,反之返回false
console.log(per instanceof Person);
注:所有对象都是Object的对象
所以任何对象和Object作instanceof检查时都会返回true
this的情况:
1.当以函数的形式调用时,this是window
2.当以方法的形式调用时,谁调用方法this就是谁
3.当以构造函数的形式调用时,this就是新创建的那个对象
构造函数修改
创建一个Person构造函数
在Person构造函数中,为每一个对象都添加了一个sayName方法,
目前我们的方法是在构造函数内部创建的,
也就是构造函数每执行一次就会创建一个新的sayName方法也是所有实例的sayName都是唯一的。
这样就导致了构造函数执行一次就会创建一个新的方法,
执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的
完全没有必要,完全可以使所有的对象共享同一个方法
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = fun;
}
function fun(){
alert(this.name);
}
var per = new Person("wdd",3,"男");
var per2 = new Person("ddw",3,"男");
var per3 = new Person("dwd",3,"男");
// per2.sayName();
// per.sayName();
console.log(per.sayName == per2.sayName);
原型
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象
- 如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过
__proto__
(隐式原型)来访问该属性 - 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
- 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
function MyClass(){
}
//向MyClass的原型中添加属性a
MyClass.prototype.a = 123;
//向MyClass添加一个方法
MyClass.prototype.sayHello = function(){
alert("Hello");
}
var mc = new MyClass();
var mc2 = new MyClass();
mc.sayHello();
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.sayName = function(){
alert("Hello! "+this.name);
};
var per = new Person("wdd",3,"男");
var per2 = new Person("ddw",3,"男");
var per3 = new Person("dwd",3,"男");
per.sayName();
per2.sayName();
per3.sayName();
使用
in
检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function MyClass(){
}
MyClass.prototype.name = "我是一个原型";
var mc = new MyClass();
console.log("name"in mc);
可以使用对象的
hasOwnProperty()
来检查对象自身中是否含有该属性
console.log(mc.hasOwnProperty("age"));
原型对象也是对象,所以它也有原型
当我们使用一个对象的属性或方法时,会先在自身中寻找
自身中如果有,则直接使用
如果没有则去原型对象中寻找,如果原型对象中有,则使用,如果没有则去原型的原型中寻找,直到找到Object对象的原型
Object对象的原型没有原型,如果在Object中依然没有找到,则返回Undefined
toString
- 当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
- 如果我们希望在输出对象时不输出[Object Object],可以为对象添加一个toString()方法
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender"+this.gender+"]";
};
var per = new Person("wdd",3,"男");
var per2 = new Person("ddw",3,"男");
var per3 = new Person("dwd",3,"男");
console.log(per);
console.log(per2);
垃圾回收(GC)
- 就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾
这些垃圾积攒过多以后,会导致程序运行的速度过慢,
所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾 - 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象
此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。 - 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,
我们不需要也不能进行垃圾回收的操作。 - 我们需要做的只是要将不再使用的对象设置null 即可
var obj = new Object(); obj = null;
数组
数组(Array)
数组也是一个对象 它和我们普通对象功能类似,也是用来存储一些值的
不同的是普通对象是使用字符串作为属性名的。
而数组使用数字来作为索引操作元素
- 数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr);
获取数组的长度
可以使用length
属性来获取数组的长度(元素的个数)
语法:
console.log(arr.length);
对于连续的数组,使用length可以获取到数组的长度(元素的个数)
对于非连续的数组,使用length会获取到数组的最大的索引+1
尽量不要创建非连续的数组
- 修改length
如果修改的length大于原长度,则多出部分会空出来如果修改的length小于原长度,则多出的元素会被删除
索引
从0开始的整数就是索引
读取数组中的元素
语法:
数组[索引]
如果读取不存在的索引,他不会报错而是返回undefined
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr[1]);
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr[3]);
向数组的最后一个位置添加元素
语法:
数组[数组.length]=值;
使用字面量创建数组
语法:
var arr = [1, 2, 3];
使用函数创建数组
可以同时添加元素,将要添加的元素作为构造函数的参数传递
var arr = new Array(1,2,3);
console.log(arr);
区分
var arr = [10];
//创建一个只含元素10的数组
var arr = new Array(10);
//创建一个长度为10的数组
数组中的元素可以是任意数据类型,也可以是对象
arr = [undefined];
var obj = {name:"wdd"};
arr[arr.length] = obj;
arr = [{name:"wdd"},{name:"ddw"},{name:"dwd"}];
console.log(arr[1].name);
也可以是函数
arr = [function(){alert(3)},function(){alert(6)}];
arr[0]();
数组方法
push()
- 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
- 可以将要添加的元素作为方法的参数传递
这样这些元素将会自动添加到数组的末尾
arr = [1,2,3];
arr.push(6);
console.log(arr);
pop()
- 该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
arr = [1,2,3];
arr.pop();
console.log(arr);
unshift()
- 该方法向数组开头添加一个或多个元素,并返回新的数组长度
arr = [1,2,3];
arr.unshift(0);
console.log(arr);
shift()
- 该方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回
arr = [1,2,3];
arr.shift();
console.log(arr);
遍历数组
将数组中所有元素取出来
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
arr[5] = 6;
arr[6] = 7;
arr[7] = 8;
arr[8] = 9;
for(var i = 0;i < arr.length;i++){
console.log(arr[i]);
}
遍历数组练习
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender"+this.gender+"]";
};
var per = new Person("wdd",20,"男");
var per2 = new Person("ddw",10,"男");
var per3 = new Person("dwd",18,"男");
var perArr = [per,per2,per3];
function getAdult(arr){
var newArr = [];
for(var i = 0;i < perArr.length;i++){
if(perArr[i].age >= 18){
console.log(perArr[i]);
}
}
return newArr;
}
var result = getAdult(perArr);
console.log(result);
forEach()
- 该方法可以用来遍历数组
forEach()方法需要一个函数作为参数像这种函数,由我们创建但是不由我们调用的,我们称为回调函数
数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容 - 浏览器会在回调函数中传递三个参数:
- 第一个参数:就是当前正在遍历的元素
- 第二个参数:就是当前正在遍历的元素的索引
- 第三个参数:就是正在遍历的数组
var arr = ["wdd","ddw","dwd"];
arr.forEach(function(value,index,obj){
console.log(value+"|"+index+"|"+obj)
});
注:IE8及以下浏览器不支持ForEach()