Javascript高级总结

一、数据类型

数据类型分类:基本数据类型引用数据类型大整数Symbol

  • 基本数据类型:NumberStringBooleanUndefinedNull,其中UndefinedNull不常用;上述五种数据类型在typeof作用下分别返回"number"、"string"、"boolean"、"undefined“和”object";
  • 引用数据类型:Object(’Object‘、‘Function’和’Array’),在typeof作用下返回"object";
  • 大整数:BigInt,在typeof作用下返回"bigint";
  • Symbol:Symbol,在typeof作用下返回"symbol";

二、数据、内存、变量

  • 数据:将实体事物以二进制的形式存储于计算机中的’东东’;
  • 内存:我们通常所指的是临时内存(内存条通电后分配的内存空间),固态硬盘是永久存在的。内存分类为栈和堆内存空间,用于存储相应的数据类型数据;
  • 变量:由变量名和变量值组成;
    总结:数据存储于内存当中,变量是这个数据的标识符,通过这个标识符可以读写操作内存中的数据;

三、对象

对象是一个事物的属性及行为描述的封装体;

四、函数

函数是一个功能模块封装及实现的封装体;

五、构造函数之原型链

  • 一切函数都是通过new Function()产生(包括函数Function本身,Object也是通过new Function()产生);
  • A instanceof B:其结果返回true,表示确定A对象是B类的实例或者说实现了某个特定的接口,那么返回true需要满足的条件是B类的显示原型与A对象的隐式原型链有交集;下面是instanceof案例:
Function instanceof Function // true(注意:Function.__proto__ === Function.prototype,唯一一个对象本身的隐式原型和显示原型相等)
Function instanceof Object // true
Object instanceof Object // true
Object instanceof Function // true
Array instanceof Array // false
Array instanceof Function // true
Array instanceof Object // true
let arr = new Array();
arr instanceof Array // true
arr instanceof Object // true
arr instanceof Function // false
let obj = {}; // 等同于let obj = new Object();
obj instanceof Object // true
obj instanceof Function // false

六、变量提升、函数提升

  • var来声明的变量名存在变量提升:
console.log(a); // undefined
var a = '变量提升';
  • 用此方式声明的函数存在函数提升:
a(); // '函数提升'
function a() {
   console.log('函数提升');
}
  • 先变量提升,后函数提升
var c = 1;
function c(c) {
	console.log(c);
}
c(2); // 报错:c is not a function
  • var声明的变量挂载于何处?
if(!(b in window)) {
	var b = 1;
}
console.log(b); // undefined 因为非模块化环境下运行,var存在变量提升并且没有代码块这个概念,所以上面语句先`var b;`(var声明的变量会在全局变量挂载和window对象下挂载这个属性,所以此时打印window.b为undefined),然后`if(!(b in window)) {b=1}`,最后打印`console.log(b)`为undefined;如果是在模块化环境下运行,在运行到if判断语句时就会报出b is not defined;

七、执行上下文、执行上下文栈

  • 执行上下文: 全局执行上下文、函数执行上下文和eval函数执行上下文(此上下文不推荐使用及产生);
  • 执行上下文栈:由全局执行上下文和函数执行上下文的执行形成的一个栈结构,遵循后进先出原则;
    **注意:**执行上下文栈存在“栈溢出”,造成这样的结果是函数多次递归调用的原因;

八、作用域、作用域链

  • 作用域就是一个封闭的空间,外界不能读写这个空间的数据(闭包除外);
  • 作用域的产生:全局作用域和代码块、函数形成的局部作用域;
  • 作用域与执行上下文的区别:作用域是“静态的”,执行上下文是“动态的”。所谓静态就是代码在运行前,作用域就已经产生(n+11代表的是全局作用域,n代表的是局部作用域),函数所能访问到的上层作用域在函数声明定义时就已经确定了,函数声明在哪里,上层作用域就在哪,和函数在哪里调用没有任何关系(闭包);所谓动态就是执行上下文是在代码运行过程中产生(n+11代表全局执行上下文,n代表函数执行上下文和eval函数执行上下文)。
function test() {
	x = 999;
	function a() {
		console.log(x);
	}
	function b() {
		var x = 888;
		a();
	}
	b();
}
test(); // 999
console.log('执行完');
/*
打印999的原因函数a在声明定义时,作用域就已经形成,对于a函数在何处调用是不会影响到a函数的作用域的,访问x的变量时,在a函数作用域内查找不到x变量,则会沿着作用域链向上查找x变量,在test函数作用域内查找到x变量打印输出。
*/

上述拓展与总结知识:
作用域链: 在程序执行前,作用域产生4个,分别是全局作用域、test函数作用域、a函数作用域和b函数作用域。4个作用域形成两条作用域链,window-test-awindow-test-b
执行上下文栈: 程序执行过程中最多产生4个执行上下文,程序刚开始执行,则产生一个全局执行上下文,并将全局执行上下文入栈;运行到test函数执行,产生test函数执行上下文,并将其入栈;运行到test函数里的b函数执行,产生b函数执行上下文,并将其入栈;运行到b函数里的a函数执行,产生a函数执行上下文,并将其入栈;至此执行上下文栈中存在4个执行上下文。a函数执行完,a函数执行上下文出栈,现在执行上下文栈中还有3个执行上下文,之后依次b函数执行上下文出栈、test函数执行上下文出栈,最后程序代码全部执行完,全局执行上下文出栈。

九、闭包

  • 闭包简单定义就是内部函数嵌套
  • 闭包形成的条件有三个:1、函数嵌套函数,2、内部函数访问或操作外部函数数据,3、外部函数调用;
  • 闭包的作用:1、延长局部变量的生命周期;2、使得上层作用域可以访问下层作用域数据;3、保护内部数据的私有性(js模块化编程),外部对于内部数据访问和操作取决于闭包的写法;
// 假定有三个button标签,打印每个标签的索引值
	var btns = document.querySelectorAll("button");
    var arr = [...btns];
    const { length } = arr;
    for (var i = 0; i < length; i++) {
       arr[i].onclick = function () {
          console.log(i + 1);
      };
    }
    // 点击三个button按钮,打印全是4;因为var声明的变量在代码块里没有作用域,for循环执行完后,i的值为3,则回调函数执行打印全为4;
    /*
    解决方案一
    */
    var btns = document.querySelectorAll("button");
    var arr = [...btns];
    const { length } = arr;
    for (let i = 0; i < length; i++) {
       arr[i].onclick = function () {
          console.log(i + 1);
      };
    }
    // 点击三个button按钮,依次打印1,2,3;因为let声明的变量在代码块里具有作用域;
    /*
    解决方案二
    */
    var btns = document.querySelectorAll("button");
    var arr = [...btns];
    const { length } = arr;
    for (var i = 0; i < length; i++) {
       arr[i].index = i;
       arr[i].onclick = function () {
          console.log(this.index + 1);
      };
    }
    // 点击三个button按钮,依次打印1,2,3;在for循环时给每个dom节点对象绑定了一个index索引,在点击时,回调函数里的this指向当前点击的dom节点对象,故可以拿到当前点击的按钮索引值;
     /*
    解决方案三
    */
    var btns = document.querySelectorAll("button");
    var arr = [...btns];
    const { length } = arr;
    for (var i = 0; i < length; i++) {
       (arr[i].onclick = function () {
          console.log(i + 1);
       })(i);
    }
    // 点击三个button按钮,依次打印1,2,3;这个运用了闭包,保证了词法作用域内的i变量不会被销毁,那么当回调函数执行时,打印的就是外层函数传递下来的i值。
  • 注意:for循环下的闭包
/*
案例一
*/
function count() {
	const arr = [];
	for(var i = 0; i < 3; i++) {
		arr.push(function() {
			return i;
		});
	}
	return arr;
}
var fns = count();
var excute0 = fns[0];
var excute1 = fns[1];
var excute2 = fns[2];
console.log(excute0(), excute1(), excute2()); // 3, 3, 3

/*
案例二
*/
function count() {
   const arr = [];
   for (var i = 0; i < 3; i++) {
     (function (i) {
       arr.push(function () {
         return i;
       });
     })(i);
   }
   return arr;
 }
var fns = count();
var excute0 = fns[0];
var excute1 = fns[1];
var excute2 = fns[2];
console.log(excute0(), excute1(), excute2()); // 0, 1, 2

/*
案例三
*/
function count() {
  var arr = [];
   var i = 1;
   arr.push(function () {
     return ++i;
   });
   return arr;
 }

 var results = count();
 var f1 = results[0];
 console.log(f1()); // 2
 console.log(f1()); // 3
 console.log(f1()); // 4
 /* 总结:三个案例都形成了闭包,在案例一中,我们在看到执行结果前,可能会误认为打印的结果是案列二的
 结果,然而并不是,结果却是3, 3, 3。因为for循环执行完i为3,所以执行arr每一项函数时,函数体打印的i
 是去函数定义的地方所在的作用域查找,没有查找到i变量,则会去该作用域的上层作用域查找,此时上层作用
 域中的i因为闭包特性,数据结构不会自动释放,所以打印3。在案例二中,我们看到打印的结果是0, 1, 2。因
 为arr中的函数调用时,也是去函数定义的地方所在的作用域查找,查找到的i变量是外层函数传递下来的变量
 值i,此处形参i,属于‘值传递’,所以打印的是当前作用域内的形参i的值,与上层作用域的i值为3是不一样
 的,此处i带有迷惑性。不妨思考一下这里函数的传参,如果是‘引用传递’,结果可能又是另外一个结果;在
 案例三中,没有for循环存在,打印的i值就是上层作用域i值;*/
  • 闭包的自动释放与手动释放:
    自动释放

    function test() {
        var a = 1;
        function excute() {
            console.log(++a);
        }      
    }
    test();  // 函数执行完,闭包就自动释放了
    console.log('闭包')
    

    手动释放

    function test() {
        var a = 1;
        function excute() {
            console.log(++a);
        }
        return excute;       
    }
    let cu1 = test();
    let cu2 = test();
    cu1(); // 2 (闭包存在)
    cu1(); // 3 (闭包存在)
    cu2(); // 2 (闭包存在)
    cu1 = null; // cu1对象重新赋值,cu1闭包手动释放,cu2闭包还存在;
    console.log('闭包')
    
  • 闭包经典应用:防抖与节流;

  • 经典面试题:

function fun(n, o){
	console.log(o);
	return {
		fun: function(m){
			return fun(m, n);
		}
	}
}

var a = fun(0); // undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0

var b = fun(0).fun(1).fun(2).fun(3); // undefined 0, 1, 2

var c = fun(0).fun(1); // undefined
c.fun(2); // 1
c.fun(3); // 1

十、对象集成(函数集成)

function Sup(name, age){
	this.name = name;
	this.age = age;
}

Sup.prototype.printSupProps = function(){
	console.log(this.name, this.age);
}

function Sub(name, age, hobby){
	Sup.call(name, age); // 第一步:保证Sub(子类)实例可以访问和操作Sup(父类)属性;
	this.hobby = hobby;
}

Sub.prototype = new Sup(); // 第二步:保证Sub(子类)实例可以访问和操作Sup(父类)方法;

Sub.prototype.constructor = Sub; // 第三步:保证Sub(子类)实例访问到的constructor属性值为Sub;

Sub.prototype.printSubProps = function(){
	console.log(this.name, this.age, this.hobby);
}

var sub = new Sub('唐三', 18, '小舞');
console.log(sub.name, sub.age, sub.hobby); // '唐三', 18, '小舞'
sub.printSubProps();
sub.printSupProps();

**注意:**组合继承对于父类的静态属性和静态方法子类不能继承。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页