======================================================
/*
* 变量的声明提前
* 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),但是如果声明变量时不适用var关键字,则变量不会被声明提前
*
* 函数的声明提前
* 使用函数声明形式创建的函数 function 函数(){},它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数
* 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
*/
console.log("a = "+a); //a=undefined
var a = 123;
fun();
//函数声明式,会被提前创建
function fun(){
console.log("我是一个fun函数");
}
//函数表达式,不会被提前创建
var fun2 = function(){
console.log("我是fun2函数");
};
fun2();
//创建一个变量
var a = 10;
function fun(){
var a = "我是fun函数中的变量a";
var b = 20;
console.log("a = "+a); //a=我是fun函数中的变量a
function fun2(){
console.log("a = "+window.a); //a=10
}
fun2();
}
fun();
console.log("b = "+b); //报错:Uncaught ReferenceError: b is not defined
/*
* 在函数作用域也有声明提前的特性,
* 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
* 声明式函数也会在函数中所有的代码执行之前执行
*/
function fun3(){
fun4();
console.log(a); //a=undefined
var a = 35;
function fun4(){
alert("I'm fun4");
}
}
fun3();
/*
* 在函数中,不使用var关键字声明的变量都会成为全局变量
*/
var c = 33;
function fun5(){
console.log("c = "+c); //c=33
c = 10;
//d没有使用var关键字声明,则会设置为全局变量
d = 100;
}
fun5();
console.log("c = "+c); //c=10;
//在全局输出c
console.log("d = "+d); //d=100
var e = 23;
/*
* 定义形参就相当于在函数作用域中声明了变量
*/
function fun6(e){
//相当于声明变量并且赋值;如var e=100;
alert("e="+e);
}
fun6(); //弹出e=undefined
fun6(100); //弹出e=100
var a = 123;
function fun(){
alert("a="+a); //a=123;
}
fun();
var a = 123;
function fun(){
alert("a="+a); //a=undefined
var a = 456;
}
fun();
alert("a="+a); //a=123;
var a = 123;
function fun(){
alert("a="+a); //a=123;
a = 456;
}
fun();
alert("a="+a); //a=456;
var a = 123;
function fun(a){
alert("a="+a);
a = 456;
}
fun(); //弹出a=undefined;
alert("a="+a); //a=123;
var a = 123;
function fun(a){
alert("a="+a);
a = 456;
}
fun(110); //弹出a=110;
alert("a="+a); //a=123;
/*
*解析器在调用函数每次都会向函数内部传递进一个隐含的参数,
*这个隐含的参数就是this,this指向的是一个对象,个对象我们称为函数执行的 上下文对象.
*根据函数的调用方式的不同,this会指向不同的对象
* 1.以函数的形式调用时,this永远都是window
* 2.以方法的形式调用时,this就是调用方法的那个对象
*/
function fun(){
console.log(this.name);
}
var name="全局变量中的name";
//创建一个对象
var obj = {
name:"孙悟空",
sayName:fun
};
var obj2 = {
name:"沙和尚",
sayName:fun
};
//以函数形式调用,this指向的是window;
fun(); //全局变量中的name
//以方法的形式调用,this是调用方法的对象
obj.sayName(); //孙悟空
obj2.sayName(); //沙和尚
/*
* push()
* 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
* 可以将要添加的元素作为方法的参数传递,这些元素将会自动添加到数组的末尾
* 该方法会将数组新的长度作为返回值返回
*/
var arr = ["孙悟空","猪八戒","沙和尚"];
var result=arr.push("唐僧","蜘蛛精","白骨精","玉兔精");
console.log("result="+result); //result=7
console.log("arr="+arr); //arr=孙悟空,猪八戒,沙和尚,唐僧,蜘蛛精,白骨精,玉兔精
/*
* pop()
* 该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
*/
var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.pop();
console.log("result = "+result); //result = 沙和尚
console.log("arr="+arr); //arr=孙悟空,猪八戒
/*
* unshift()
* 向数组开头添加一个或多个元素,并返回新的数组长度
* 向前边插入元素以后,其他的元素索引会依次调整
*/
var arr = ["孙悟空","猪八戒","沙和尚"];
var result=arr.unshift("牛魔王","二郎神");
console.log("result = "+result); //result = 5
console.log("arr="+arr); //arr= 牛魔王,二郎神,孙悟空,猪八戒,沙和尚
/*
*shift()
*可以删除数组的第一个元素,并将被删除的元素作为返回值返回
*/
var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.shift();
console.log("result = "+result); //result = 孙悟空
console.log("arr="+arr); //arr= 猪八戒,沙和尚
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
//常用方式;
for(var i=0;i
/*
*slice()可以用来从数组提取指定元素
*注意:该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
*参数:
*1.截取开始的位置的索引,包含开始索引
*2.截取结束的位置的索引,不包含结束索引
* 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
* 索引可以传递一个负值,如果传递一个负值,则从后往前计算
* -1 表示倒数第一个
* -2 表示倒数第二个
*/
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result=arr.slice(1,3);
//var result=arr.slice(2,-1);
//var result=arr.slice(1);
console.log("result="+result); //result=猪八戒,沙和尚
console.log("arr="+arr); //arr=孙悟空,猪八戒,沙和尚,唐僧,白骨精
/*
*splice() 向/从数组中添加/删除项目,然后返回被删除的项目
*注意:使用splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
*参数:
* 第一个表示开始位置的索引
* 第二个表示删除的数量
* 第三个及以后可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
*/
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result=arr.splice(1,2);
console.log("result="+result); //result=猪八戒,沙和尚
console.log("arr="+arr); //arr=孙悟空,唐僧,白骨精
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result=arr.splice(1,3,"英国","德国","中国","美国");
console.log("result="+result); //result=猪八戒,沙和尚,唐僧
console.log("arr="+arr); //arr=孙悟空,英国,德国,中国,美国,白骨精
/*
* concat()可以连接两个或多个数组,并将新的数组返回
* - 该方法不会对原数组产生影响
*/
var arr=["UK","USA","Canada"];
var arr2=["China","Russia","India"];
var result=arr.concat(arr2);
console.log("result="+result); //result=UK,USA,Canada,China,Russia,India
/*
* join()可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回
* 注意:在join()中可以指定一个字符串作为数组中元素的连接符
* 如果不指定连接符,则默认使用,作为连接符
*/
var arr=["UK","USA","Canada"];
var result=arr.join("--");
console.log("result="+result); //result=UK--USA--Canada
console.log("arr="+arr); //arr=UK,USA,Canada
/*
* reverse()
* - 该方法用来反转数组(前边的去后边,后边的去前边)
* - 该方法会直接修改原数组
*/
var arr = ["UK", "USA", "Canada"];
arr.reverse();
console.log("arr=" + arr); //arr=Canada,USA,UK
/*
*在sort()可以添加一个回调函数来指定排序规则,回调函数中需要定义两个形参
* 浏览器会根据回调函数的返回值来决定元素的顺序,
* 如果返回一个大于0的值,则元素会交换位置
* 如果返回一个小于0的值,则元素位置不变
* 如果返回一个0,则认为两个元素相等,不交换位置
* 如果需要升序排列,则返回 a-b
* 如果需要降序排列,则返回b-a
*/
var arr=[12,34,23,87,38,65];
arr.sort(function(a,b){
return a-b;
});
console.log("arr="+arr); arr=12,23,34,38,65,87
/*
* call()和apply()
* - 这两个方法都是函数对象的方法,需要通过函数对象来调用
* - 当对函数调用call()和apply()都会调用函数执行
* - 在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this
* - call()方法可以将实参在对象之后依次传递
* - apply()方法需要将实参封装到一个数组中统一传递
*
* - this的情况:
* 1.以函数形式调用时,this永远都是window
* 2.以方法的形式调用时,this是调用方法的对象
* 3.以构造函数的形式调用时,this是新创建的那个对象
* 4.使用call和apply调用时,this是指定的那个对象
*/
function fun(a,b){
console.log("a="+a);
console.log("b="+b);
console.log(this);
}
//fun();
//fun.call();
//fun.apply();
var person={
name:"tom",
sayName:function(){
alert(this.name);
}
}
//fun.call(person,2,3);
fun.apply(person,[2,3]); //call()和apply()的区别是call()方法依次传入参数就可以,apply()方法必须将参数转化为数组传入;
/*
* 在调用函数时,浏览器每次都会传递进两个隐含的参数:
* 1.函数的上下文对象 this
* 2.封装实参的对象arguments
* - arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
* - 在调用函数时,我们所传递的实参都会在arguments中保存
* - arguments.length可以用来获取实参的长度
* - arguments[0] 表示第一个实参
* - arguments[1] 表示第二个实参 。。。
* - arguments里有一个属性叫做callee,
* 这个属性对应一个函数对象,就是当前正在指向的函数的对象
*/
function fun(a,b){
console.log("arguments="+arguments); //arguments=[object Arguments]
console.log(arguments.length+";"+arguments[0]+";"+arguments[1]); //2;2;3
console.log(typeof arguments); //object
console.log(arguments instanceof Array); //false
console.log(Array.isArray(arguments)); //false
console.log(arguments.callee==fun); //true
}
fun(2,3);
//for...in 语句(对象中有几个属性,循环体就执行几次)
var person={
name:"冯朗",
age:25,
sex:"男"
}
for(var prop in person){ //每次执行时都会将对象中一个属性的名字赋给变量
console.log(prop); //name、age、sex
}
/*
* 创建一个Person构造函数
* - 在Person构造函数中,为每一个对象都添加了一个sayName方法,
* 目前我们的方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法
* 也就是说所有实例的sayName都是唯一的,这样就导致了构造函数执行一次就会创建一个新的方法.
* 这是完全没有必要,完全可以使所有的对象共享同一个方法
*/
function Person(name , age , gender){
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
//this.sayName = fun;
}
//将sayName方法在全局作用域中定义
/*
* 将函数定义在全局作用域,污染了全局作用域的命名空间
* 而且定义在全局作用域中也很不安全
*/
function fun(){
alert("Hello大家好,我是:"+this.name);
};
//向原型中添加sayName方法
Person.prototype.sayName = function(){
alert("Hello大家好,我是:"+this.name);
};
var person=new Person("冯朗",25,"女");
var person2=new Person("冯跃",28,"男");
person.sayName();
person2.sayName();
/*
* 原型prototype
* 利用构造函数方式创建的每一个函数,解析器都会向函数中添加一个属性prototype
* 这个属性对应着一个对象,这个对象就是我们所谓的原型对象
* 如果函数作为普通函数调用prototype没有任何作用
* 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,
* 指向该构造函数的原型对象,我们可以通过__proto__来访问该属性
*
* 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,
* 我们可以将对象中共有的内容,统一设置到原型对象中。
*
* 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,
* 如果没有则会去原型对象中寻找,如果找到则直接使用
*
* 以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,
* 这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了
*/
function MyClass(){
}
//向MyClass的原型中添加属性a
MyClass.prototype.a = "我是prototype中的a";
//向MyClass的原型中添加一个方法
MyClass.prototype.sayHello = function(){
alert("hello");
};
var mc = new MyClass();
var mc2 = new MyClass();
//console.log(MyClass.prototype); //[object Object]
//console.log(mc2.__proto__ == MyClass.prototype); //true
//向mc中添加a属性
mc.a = "我是mc中的a";
//console.log(mc.a); //我是mc中的a
//console.log(mc2.a); //我是prototype中的a
//mc.sayHello();
mc2.sayHello();
/*
* 创建一个构造函数
*/
function MyClass(){
}
//向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
var mc = new MyClass();
mc.age = 18;
//console.log(mc.name); //我是原型中的名字
//使用in检查对象中是否含有某个属性时,如果对象属性中没有但是原型中有也返回true
//console.log("name" in mc); //true
//console.log("age" in mc); //true
//可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
//使用该方法只有当对象自身中含有属性时,才会返回true
//console.log(mc.hasOwnProperty("age")); //true
//console.log(mc.hasOwnProperty("name")); //false
/*
*原型对象也是对象,它也有原型
*当我们使用一个对象的属性或方法时首先会现在自身中寻找,
* 如果自身中如果有则直接使用
* 如果没有则去原型对象中寻找,如果原型对象中有则使用
* 如果没有则去原型的原型中寻找,直到找到Object对象的原型
* Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined
*/
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true
将函数作为另一个函数的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
setTimeout(function() {
alert(msg)
}, time)
}
showDelay('Hello,World', 2000);
1、使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2、让函数外部可以操作(读写)到函数内部的数据(变量/函数)
function fn1() {
var a = 2
function fn3() {
a--
console.log(a)
}
return fn3
}
var f = fn1();
f() // 1
f() // 0
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡:在嵌套的内部函数成为垃圾对象时
function fn1() {
//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
缺点:函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长,容易造成内存泄露
解决方案: 能不用闭包就不用;及时释放;
function fn1() {
var arr = new Array[100000]
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //让内部函数成为垃圾对象-->回收闭包
方案一:
function myModule() {
//私有数据
var msg = 'My atguigu'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
方案二(该方案最佳):
(function () {
//私有数据
var msg = 'My atguigu'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()