• 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介绍

  • 相关阅读:
    深度神经网络压缩与加速技术
    Postgresql源码(74)两阶段事务PrepareTransaction事务如何与会话解绑(下)
    Facebook平台特征概述
    数据加密标准(DES)的C++语言描述实现
    在springboot工程中修改使用quartz创建的定时任务
    MindSpore:【model_zoo】【resnet】尝试用THOR优化器运行时报cannot import name ‘THOR‘
    Java框架学习顺序,主要讲解三大框架
    redis常用5大数据类型
    YOLOv5-PTQ量化部署
    springboot:xml配置信息
  • 原文地址:https://blog.csdn.net/2301_80480494/article/details/139026790