js高级--

执行上下文与执行上下文栈

变量提升与函数提升

1.变量的声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到,值为:undefined

2.函数声明提升
通过function声明的函数,在之前就可以直接调用,值为:函数定义(对象)
3.问题:变量提升和函数提升是如何产生的?

在这里插入图片描述

<script>
         /* 面试题 */
        //  输出a的值
        var a = 3;
        function fn(){
            console.log(a);
            var a = 4;
        }

        fn(); //a=undefined


        console.log(b); //undefined 变量提升
        fn2(); //可调用   函数提升
        // fn3(); 不能调用,变量提升

        var b = 3;
        function fn2(){
            console.log("fn2()");
        }

        var fn3 = function(){
            console.log("fn3()");
        };
    </script>

执行上下文

1.代码分类(位置)

  • 全局代码
  • 函数(局部)代码

2.全局执行上下文

  • 在执行全局代码前将window确定为全局执行上下文
    -对全局数据进行预处理
  • var 定义的全局变量–>undefined,添加为window的属性
  • function声明的全局函数–>赋值(fun),添加为window的方法
  • this–>赋值(window)
    开始执行全局代码

函数执行上下文

在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)

对局部数据进行预处理

  • 形参变量–>赋值(实参)–>添加为执行上下文的属性
  • arguments–>赋值(实参列表),添加为执行上下文属性
  • var 定义的局部变量–>undefined,添加为执行上下文的属性
  • function声明的函数–>赋值(fun),添加为执行上下文的方法
  • this–>赋值(调用函数的对象)
  • 开始执行函数体代码

在这里插入图片描述
执行的时候,如果设置断点,可以看到,functiona2()是被跳过的,因为一开始执行了,就不会再执行了。
在这里插入图片描述

执行上下文栈

1.在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3.在函数执行上下文创建后,将其添加到栈中(压栈)
4.在当前函数执行完后,将栈顶的对象移除(出栈)
5.当所有的代码执行完之后,栈中只剩下window

执行上下文对象,是在调用bar的时候产生的,一共n+1次,是指n次调用function+1次调用window
在这里插入图片描述
当前执行的一定是栈顶的,执行完栈顶,再向下执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

执行上下文测试题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作用域与作用域链

作用域

在这里插入图片描述
在这里插入图片描述

作用域与执行上下文

作用域与执行上下文的区别:
区别1

  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时,
  • 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
  • 函数执行上下文是在调用函数时,函数体代码执行之前创建

区别2

  • 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
  • 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放

联系

  • 上下文环境(对象)是从属于所在的作用域
  • 全局上下文环境–>全局作用域
  • 函数上下文环境–>对应的函数作用域

在这里插入图片描述
在这里插入图片描述

作用域链

在这里插入图片描述
在这里插入图片描述

作用域测试题

在这里插入图片描述
在这里插入图片描述

闭包

引入

在这里插入图片描述

闭包的概念

在这里插入图片描述
在这里插入图片描述

闭包的作用

在这里插入图片描述

闭包的生命周期

在这里插入图片描述

闭包的应用

需要先创建一个js模块,把这些代码放在js中
通过return 向外暴露,前面的doSomething 是一个字符串(就是外面调用方法的时候的方法名) 后面的doSomething是一个函数名
在这里插入图片描述
通过匿名函数自定义向外暴露
Q:怎么样向外暴露,把想暴露的属性添加到window中
这个“实参”写window,实现代码的压缩(比如我可以直接写一个w)
****

闭包的应用

在这里插入图片描述
在这里插入图片描述

闭包的缺点

在这里插入图片描述

内存溢出与内存泄露

在这里插入图片描述

闭包测试题

测试题1:
()()代表访问里面的函数体
在这里插入图片描述
测试题2:
在这里插入图片描述

原型与原型链:

显式&隐式

原形和原型链的学习链接!!超赞笔记
请添加图片描述

在这里插入图片描述

原型链属性

原型链属性链接

执行上下文与执行上下文栈:

变量提升与函数提升

变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined)
函数提升: 在函数定义语句之前, 就执行该函数
先有变量提升, 再有函数提升

理解

执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性
执行上下文栈: 用来管理产生的多个执行上下文

分类:

全局: window
函数: 对程序员来说是透明的

生命周期

全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
函数 : 调用函数时产生, 函数执行完时死亡

包含哪些属性:
全局 :

用var定义的全局变量 ==> undefined
使用function声明的函数 ==> function
this ==> window

函数

用var定义的局部变量 ==> undefined
使用function声明的函数 ==> function
this ==> 调用函数的对象, 如果没有指定就是window
形参变量 ==> 对应实参值
arguments ==> 实参列表的伪数组

执行上下文创建和初始化的过程

全局:
	在全局代码执行前最先创建一个全局执行上下文(window)
	收集一些全局变量, 并初始化
	将这些变量设置为window的属性
函数:
	在调用函数时, 在执行函数体之前先创建一个函数执行上下文
	收集一些局部变量, 并初始化
	将这些变量设置为执行上下文的属性

作用域与作用域链:

理解:

作用域: 一块代码区域, 在编码时就确定了, 不会再变化
作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量

分类:

全局
函数
js没有块作用域(在ES6之前)

作用

作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
作用域链: 查找变量

区别作用域与执行上下文

作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系: 执行上下文环境是在对应的作用域中的

对象高级

对象创建模式

Object构造函数模式

在这里插入图片描述

对象字面量

在这里插入图片描述

工厂模式

在这里插入图片描述

自定义构造函数模式

在这里插入图片描述

构造函数+原型的组合模式

在这里插入图片描述

继承模式

原型链继承

在这里插入图片描述

请添加图片描述

借用构造函数继承

在这里插入图片描述

组合继承

  • call方法那个地方相当于this.person(name,age){…}但是这句话是不能写的, 因为一开始是没有person函数的、、
  • call代表的是借用执行,this来执行person,this指的是new出来的S

在这里插入图片描述

对象高级总结

对象的创建模式:

  • Object构造函数模式
var obj = {};
obj.name = 'Tom'
obj.setName = function(name){this.name=name}

  • 对象字面量模式
var obj = {
  name : 'Tom',
  setName : function(name){this.name = name}
}

  • Object构造函数模式
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.setName = function(name){this.name=name;};
}
new Person('tom', 12);

  • 构造函数+原型的组合模式
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.setName = function(name){this.name=name;};
new Person('tom', 12);

继承模式:

原型链继承

  • 原型链继承 得到方法
function Parent(){}
Parent.prototype.test = function(){};
function Child(){}
Child.prototype = new Parent(); // 子类型的原型指向父类型实例
Child.prototype.constructor = Child
var child = new Child(); //有test()

  • 借用构造函数 : 得到属性
function Parent(xxx){this.xxx = xxx}
Parent.prototype.test = function(){};
function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数   this.Parent(xxx)
}
var child = new Child('a', 'b');  //child.xxx为'a', 但child没有test()

  • 组合
function Parent(xxx){this.xxx = xxx}
Parent.prototype.test = function(){};
function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数   this.Parent(xxx)
}
Child.prototype = new Parent(); //得到test()
var child = new Child(); //child.xxx为'a', 也有test()

进程与线程

  1. 进程:程序的一次执行,它战友一片独有的内存空间
  2. 线程:是进程内一个独立的执行单元,是程序执行的一个完整流程,是CPU最小 的调度单元
    在这里插入图片描述
  3. 一个进程里面,一个线程:单线程,多个进程:多线程
  4. 程序运行在某个进程某个线程上

**在这里插入图片描述**
在这里插入图片描述

浏览器内核

在这里插入图片描述

定时器引发的思考

1.定时器真的是定时执行的吗?

  • 定时器不能保证真正定时执行
  • 一般会延迟一点点,也有可能延长很长时间

2.定时器回调函数是在分线程执行的吗?
在主线程执行的,js是单线程的
在这里插入图片描述

在这里插入图片描述
加上长时间执行那个程序之后,定时器执行的时间就说不准了

js是单线程执行的

为什么js是单线程
JS是单线程的原因主要和JS的用途有关,JS主要实现浏览器与用户的交互,以及操作DOM。
如果JS被设计为多线程,如果一个线程要修改一个DOM元素,另一个线程要删除这个DOM元素,这时浏览器就不知道该怎么办,为了避免复杂的情况产生,所以JS是单线程的。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

在这里插入图片描述
加上了一个时间为0的(初始化代码->回调代码)
在这里插入图片描述
1.如何证明js执行是单线程的?

  • setTimeout()的回调函数是在主线程执行的
  • 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行

2.为什么js要用单线程模式,而不是用多线程模式?

  • JavaScript的单线程,与它的用途有关
  • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM
  • 这决定了它只能是单线程,否则会带来很复杂的同步问题

3.代码的分类:

  • 初始化代码
  • 回调代码

4.js引擎执行代码的基本流程

  • 先执行初始化代码:包含一些特别的代码 回调函数(异步执行)
    异步执行指的是某些函数代码,必须在所有的初始化代码执行完之后才执行
  • 设置定时器
  • 绑定事件监听
  • 发送ajax请求
  • 后面在某个时刻才会执行回调代码

事件循环模型

在这里插入图片描述

  1. stack栈,栈中存放的对象
  2. 定时器肯定不会有问题的,到了一定的时间点,就会把回调函数放在下面的callback队列当中(待执行),如果前面初始化代码执行很长时间,回调函数就会延迟等待。
  3. 初始化代码执行完了之后才有可能执行回调代码
    在这里插入图片描述
    在这里插入图片描述

H5 Web Workers多线程

在这里插入图片描述
未使用H5线程
在这里插入图片描述
主线程

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<script type="text/javascript">
		window.onload=function(){
			var input = document.getElementById("number");
		var btn = document.getElementById("btn");
		btn.onclick = function () {

			var number = input.value;
			//console.log(number);
			//创建一个worker对象(链接分线程)
				var worker=new Worker("./script/worker.js");

				//向分线程发送消息
				worker.postMessage(number);
				console.log("主线程向分线程发送的数据: "+number);
			//function是异步的,不论写在那,都在postMessage之后(因为它是初始化代码,function是回调函数)
			//接受消息的节点
				worker.onmessage=function(event){
					console.log("主线程向分线程返回的数据 "+event.data);
console.log(this);
			}
		}
		}
	</script>
</head>

<body>
	<input type="text" name="" id="number" placeholder="数值">
	<button id="btn">返回结果</button>
</body>

</html>

分线程

//分线程

function fab(n){
    return n<=2 ? 1 : fab(n-1)+fab(n-2);
}
//这个地方写this.message=也行
var onmessage=function(event){
  
    var number=event.data;
    console.log("分线程接受主线程发送的数据: "+number);
var result=fab(number);
postMessage(result);
console.log("分线程计算完毕,返回结果 "+result);

//alert(result);alert是window的方法,调用会出错,分线程不可以使用

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值