JavaScript知识点总结

1、作用域是指一个变量的作用的范围,在JS中主要有两种作用域:全局作用域和局部作用域

1.1全局作用域

  • 直接编写在script标签中的JS代码,都在全局作用域;
  • 全局作用域在页面打开时创建,在页面关闭时销毁;
  • 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
  • 在全局作用域中创建的变量都会作为window对象的属性保存,创建的函数都会作为window对象的方法保存
  • 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到

======================================================

/*
*  变量的声明提前
 * 使用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();

1.2函数作用域

  • 调用函数时创建函数作用域,函数执行完毕以后函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
  • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
  • 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用;如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错ReferenceError
  • 在函数中要访问全局变量可以使用window对象

//创建一个变量
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

1.3作用域举例测试

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;

2、JavaScript中this关键字

/*
 *解析器在调用函数每次都会向函数内部传递进一个隐含的参数,
 *这个隐含的参数就是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();  //沙和尚

3、JavaScript中的数组

  • push( )

/*
* push()
 * 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
 * 可以将要添加的元素作为方法的参数传递,这些元素将会自动添加到数组的末尾
 * 该方法会将数组新的长度作为返回值返回
 */
var arr = ["孙悟空","猪八戒","沙和尚"];
var result=arr.push("唐僧","蜘蛛精","白骨精","玉兔精");

console.log("result="+result);    //result=7
console.log("arr="+arr);      //arr=孙悟空,猪八戒,沙和尚,唐僧,蜘蛛精,白骨精,玉兔精
  • pop( )

/*
* pop()
 * 该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
 */
var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.pop();

console.log("result = "+result);   //result = 沙和尚
console.log("arr="+arr);   //arr=孙悟空,猪八戒
  • unshift( )

/*
* unshift()
 * 向数组开头添加一个或多个元素,并返回新的数组长度
 * 向前边插入元素以后,其他的元素索引会依次调整
 */
 var arr = ["孙悟空","猪八戒","沙和尚"];
var result=arr.unshift("牛魔王","二郎神");

console.log("result = "+result);   //result = 5
console.log("arr="+arr);   //arr= 牛魔王,二郎神,孙悟空,猪八戒,沙和尚
  • shift( )

/*
*shift()
*可以删除数组的第一个元素,并将被删除的元素作为返回值返回
*/ 
var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.shift();

console.log("result = "+result);   //result = 孙悟空
console.log("arr="+arr);   //arr= 猪八戒,沙和尚
  • 数组的遍历

var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
	
//常用方式;
for(var i=0;i

/*
* 在调用函数时,浏览器每次都会传递进两个隐含的参数:
 * 	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
}

5、JavaScript中的原型

/*
 * 创建一个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 

JavaScript知识点总结_第1张图片

6、JavaScript中的闭包

1、对闭包的初步理解

  • 如何产生闭包:当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
  • 闭包到底是什么:
    ①理解一: 闭包是嵌套的内部函数(绝大部分人)
    ②理解二: 包含被引用变量(函数)的对象(极少数人)
    注意: 闭包存在于嵌套的内部函数中
  • 产生闭包的条件:
    ①函数嵌套
    ②内部函数引用了外部函数的数据(变量/函数)

2、常见的闭包

  • 将函数作为另一个函数的返回值

     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);
    

3、闭包的作用

  • 1、使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)

  • 2、让函数外部可以操作(读写)到函数内部的数据(变量/函数)

      function fn1() {
      		var a = 2
      		function fn3() {
      			a--
      			console.log(a)
      		}
      		return fn3
      	}
      var f = fn1();
      f() // 1
      f() // 0
    

4、闭包的生命周期

  • 产生:在嵌套内部函数定义执行完时就产生了(不是在调用)

  • 死亡:在嵌套的内部函数成为垃圾对象时

      function fn1() {
          //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
          var a = 2
          function fn2 () {
            a++
            console.log(a)
          }
          return fn2
        }
        var f = fn1()
        f() // 3
        f() // 4
        f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
    

5、闭包的缺点及解决方案

  • 缺点:函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长,容易造成内存泄露

  • 解决方案: 能不用闭包就不用;及时释放;

       function fn1() {
          var arr = new Array[100000]
          function fn2() {
            console.log(arr.length)
          }
          return fn2
        }
        
        var f = fn1()
        f()
        f = null //让内部函数成为垃圾对象-->回收闭包
    

7、闭包的应用实例:自定义模块

  • 方案一:

    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
        }
    })()
    

你可能感兴趣的:(JavaScript,JavaScript)