js高级—原型链/执行上下文/作用域/闭包/面向对象高级/进程与线程...

1. 原型与原型链

1.1 原型(prototype)

每一个函数都有一个prototype属性,默认指向一个Object空对象(称为原型对象)
原型对象里有一个constructor属性,指向函数对象
在这里插入图片描述
作用:函数的所有实例对象可以自动拥有原型中的属性(方法)

在这里插入图片描述

1.2 显式原型与隐式原型

  1. 每一个函数function都有一个prototype,即为显式原型(原型属性);
  2. 每个实例对象都有一个__proto__,可称为隐式原型(原型属性);
  3. 对象的隐式原型的值 是其 实例对象的显式原型的值;也就是地址指向的是同一个空的Object对象地址
function Fn(){
//内部语句:this.prototype ={}
}
var fn=new Fn()
//内部语句:this.__proto__=Fn.prototype
console.log(Fn.prototype===fn.__proto__)//true

//给原型添加方法
Fn.prototype.test=function(){
}
fn.test();

在这里插入图片描述
总结:
函数的prototype属性:在定义函数时自动添加的,默认值是一个空object对象
对象的proto属性:创建对象时自动添加的,默认值为构造函数的prototype属性值

1.3 原型链(隐式原型链)

  1. 访问一个对象属性时,
    1.先在自身属性中查找,找到返回
    2.如果没有,再沿着__proto__这条链向上查找,找到返回
    3.如果最终没找到,返回undefined
  2. 作用:查找对象的属性(方法)

构造函数/原型/实体对象的关系图解:
在这里插入图片描述

所有函数的prototype指向的object空对象都属于Object的实例,但Object本身的prototype不属于Object的实例
(所有对象的原型对象的原型对象就是Object的原型对象)

补充:
1.函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
2.所有函数都是Function的实例(包含Function自己)(Function是自己的实例,c)
3.Object的原型对象是原型链的尽头

属性问题:

1.读取对象属性的时候会自动到原型链中查找
2.设置对象属性值时:不会查找原型链,如果当前对象中没有此属性,则直接添加此属性并设置其值
3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上

1.4 探索instanceof

1.instanceof是如何判断的?
表达式:A instanceof B
如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false
2.Function是通过new自己产生的实例

1.5 面试题

在这里插入图片描述
2323

2. 执行上下文与执行上下文栈

变量提升与函数提升

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

面试题:输出undefined
var a=3
function fn(){
	console.log(a)
	var a=4
	}
fn()

在这里插入图片描述

执行上下文

  1. 代码分类:全局代码、函数(局部代码)
  2. 全局执行上下文:

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

  1. 函数执行上下文

1.在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
2.对局部数据进行预处理
·形参变量=>赋值(实参)=>添加为执行上下文的属性
·arguments==>赋值(实参列表),添加为执行上下文的属性
·var定义的局部变量==>undefined,添加为执行上下文的属性
·function声明的函数==>赋值(fun),添加为执行上下文的方法
·this==>赋值(调用函数的对象)
3.开始执行函数体代码

执行上下文栈

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

面试题

1.

在这里插入图片描述
1.输出顺序
在这里插入图片描述
2.整个过程中产生了5个执行上下文

2.

在这里插入图片描述

3.

在这里插入图片描述
if语句中的var b进行变量提升,所以b存在在window中,if中为false,不执行赋值语句。所以b为undefined

4.

在这里插入图片描述
先执行变量提升再执行函数提升,执行顺序:
在这里插入图片描述

3.作用域与作用域链

作用域

  1. 理解
    *就是一"地盘,一个代码段所在的区域
    *它是静态的(相对于上下文对象),在编写代码时就确定了
  2. 分类
    *全局作用域
    *函数作用域
    *块作用域
  3. 作用
    *隔离变量,不同作用域下同名变量不会有冲突

作用域和执行上下文

区别1:

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

区别2:

1.作用域是静态的,只要函数定义好了就一直存在,且不会再变化
2.上下文环境是动态的,调用函数时创建,函数调用结束时上下文环境就会被释放

3.联系

1.上下文环境(对象)是从属于所在的作用域
2.全局上下文环境==>全局作用域
3.函数上下文环境==>对应的函数使用域

4.闭包

js闭包详解1
闭包详解2

问题:点击按钮后弹出框第几个按钮被点击(在点击按钮之前i已经执行到5了,所以无论怎么点弹出的都是5)
    var btns=document.querySelectorAll('button');
    for (var i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click',function(){
            alert("第"+(i+1)+"个按钮被点击了")
        })
    }
    console.log(i);

两种解决方式

1.var换成let
	let btns=document.querySelectorAll('button');
    for (let i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click',function(){
            alert("第"+(i+1)+"个按钮被点击了")
        })
    }
    console.log(i);
2.利用闭包
    let btns=document.querySelectorAll('button');
    for (let i = 0; i < btns.length; i++) {
        (function(i){
            btns[i].addEventListener('click',function(){
            alert("第"+(i+1)+"个按钮被点击了")
            })
        })(i)
    }

闭包理解

1.如何产生闭包?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

2.闭包到底是什么?

理解一:闭包是嵌套的内部函数
*理解二:包含被引用变量(函数)的对象
*注意:闭包存在于嵌套的内部函数中

3.产生闭包的条件?

函数嵌套
内部函数引用了外部函数的数据(变量/函数)

4.常见的闭包

1.将函数作为另一个函数的返回值
2.将函数的实参传递给另一个函数调用

1.

在这里插入图片描述
只有一个闭包,主要是看里面有几个函数

2.

在这里插入图片描述
内置函数alert(msg)产生闭包,而time没有,因为setTimeout属于api调用函数不属于函数对象,所以不能产生闭包

闭包的作用

1.使用函数内部的变量在函数执行完后仍然存活在内存中(延长了局部变量的生命周期)
2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:

1.函数执行完后,函数内部声明的局部变量是否还存在?
一般不存在,存在于闭中的变量才可能存在
2.在函数外部能直接访问函数内部的局部变量吗?
不能,但可以通过闭包让外部操作它

闭包的生命周期

1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
2.死亡:在嵌套的内部函数成为垃圾对象时
在这里插入图片描述

闭包的应用2:定义Js模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包信n个方法的对象或函数
  • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

闭包的缺点

1.函数执行完后,函数内部的局部变量没有释放,占用内存时间会变长
2.容易造成内存泄漏

解决:
能不用闭包就不用,及时释放

内存溢出和内存泄漏

1.内存溢出

  • 一种程序运行出现的错误
  • 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
//1.内存溢出
var obj={}
for (var i=0;i<10000;i++){
	obj[i]=new Array(10000000)
	console.log('-----')
}

2.内存泄露

  • 占用的内存没有及时释放
  • 内存泄露积累多了就容易导致内存溢出
  • 常见的内存泄露:
    1.意外的全局变量
    2.没有及时清理的计时器或回调函数
    3.闭包
//2.内存泄露
//意外的全局变量
functionfn(){
	a=new Array(10000000)
	console.log(a)
	}
fn()

//没有及时清理的计时器或回调函数
var intervalId =setInterval(function({
//启动循环定时器后不清理
	console.log('----')
	},1000);
clearInterval(intervalId)

//闭包
function fn1(){
	var a=4
	function fn2(){
		console.log(++a)
	}
	return fn2
}
	var f=fn1()
	f()
	F=null

//代码片段
var name="The window";
var object = {
	name : "My object",
	getNameFunc : function(){
		return function(){
			return this.name;
	};
alert(object.getNameFunc()())//The window 没有闭包(函数嵌套,但内层函数没有用外部函数的变量)
//原因:object.getNameFunc()()调用的函数立即执行,直接执行的函数里的this指向的是window

//代码片段二
var name2 "The window";
var object2 = {
	name2 : "My object",
	getNameFunc :function(){
		var that =this;
		return function(){
			return that.name;
	};
alert(object.getNameFunc()())//	My object 有闭包
//that保存的this是 调用的getNameFunc()函数的object2 因此指向的是object2

5.面向对象高级

对象模式创建

方式一:Object.构造函数模式

1.套路:先创建空0bject对象,再动态添加属性/方法
2.适用场景:起始时不确定对象内部数据
3.问题:语句太多

//一个人:name:"Tom",age:12
var p new object()
p.name 'Tom'
p.age 12
p.setName = function (name){
	this.name =name
}

方式二:对象字面量模式

1.套路:使用}创建对象,同时指定属性/方法
2.适用场景:起始时对象内部数据是确定的
3.问题:如果创建多个对象,有重复代码

方式三:工厂模式

1.套路:通过工厂函数动态创建对象并返回
2.适用场景:需要创建多个对象
3.问题:对象没有一个具体的类型,都是0bject类型

function createPerson(name,age){//返回一个对象的函数===>工厂函数
var obj ={
	name:name,
	age:age,
	setName:function (name){
		this.name name
		}
	}
	return obj
}

方式四:自定义构造函数模式

1.套路:自定义构造函数,通过new创建对象
2.适用场景:需要创建多个类型确定的对象
3.问题:每个对象都有相同的数据,浪费内存

function Person(name,age){
	this.name name
	this.age age
	this.setName function (name){
		this.name=name
		}
}
var p1=new Person()

继承模式

原型对象继承

1.套路

1,定义父类型构造函数
2.给父类型的原型添加方法
3.定义子类型的构造函数
4.创建父类型的对象赋值给子类型的原型
5.将子类型原型的构造属性设置为子类型
6.给子类型原型添加方法
7.创建子类型的对象:可以调用父类型的方法

2.关键:子类型的原型为父类型的一个实例对象
在这里插入图片描述

借用构造函数继承(假的)

1.套路:

1.定义父类型构造函数
2.定义子类型构造函数
3.在子类型构造函数中调用父类型构造

2.关键:在子类型构造函数中通用super()调用父类型构造函数

组合继承

6.进程与线程

  1. 进程:程序的一次执行,它占有一片独有的内存空间(可以通过windows任务管理器查看进程)
  2. 线程:是进程内的一个独立执待单元;
    是程序执行的一个完整流程;
    是CPU的最小的调度单元
    在这里插入图片描述

相关知识

  • 应周程序必须运行在某个进程的某个线程上
  • 一个进程中至少有一个运行的线程:主线程,进程启动后自动创建
  • 一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
  • 一个进程内的数据可以供其中的多个线程直接共享
  • 多个进程之间的数据是不能直接共享的
  • 线程池(thread pool):保存多个线程对象的容器,实现线程对象的反复利用

相关问题:

1.何为多进程与多线程?

多进程运行:一应用程序可以同时启动多个实例运行
多线程:在一个进程内,同时有多个线程运行

2.比较单线程与多线程?

多线程:

优点:能有效提升CPU的利用率
缺点:线程间切换开销、创建多线程开销、死锁与状态同步问题

单线程:

优点:顺序编程简单易懂
缺点:效率低

3.js是单线程还是多线程?

单线程,但使用H5中的Web Workers可以多线程运行

4.浏览器运行是单线程还是多线程、是单进程还是多进程?

多线程运行;有单进程也有多进程

7.浏览器内核

支撑浏览器运行的最核心的程序

不同的浏览器可能不一样

内核由很多模块组成:
在这里插入图片描述

8.定时器

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

*定时器并不能保证真正定时放行
*一般会延迟一丁点(可以接受),也有可能延迟很长时间

2.定时器回调函数是在哪个线程执行的?

在主线程执行的,js是单线程的

3.定时器是如何实现的?

事件循环模型

9.webworkers

web workers

web worker介绍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值