js高级进阶-重点难点

4 篇文章 0 订阅

js高级进阶部分

数组类型:
typeof能判断的部分只有 number/String/boolean/undefine/Array/function/Object
判断是否null或undefined直接用===判断就好不需要typeof,不能判断object与null、object与array
a instanceof b a类型是不是b类型的实例 instanceof返回truefalse

aa={
a:function(){
return function(){
return ‘aaa’
}
}
}
console.log(a()()) ->aaa


相关问题
undefined:定义了未赋值
null:定义了也赋值了,只不过赋值的是null
函数里
obj.name=‘A’
obj = {name:A} 这两者是不一样的,前者可以改变变量里的值,后者并不能改变变量里的值

js管理内存
释放内存(清空内存中的数据,标识内存可以分配使用(不释放就不能复用))
变量在定义后会暂时存储,在执行完函数后会自动释放(栈空间的局部变量),最后用完之后会成为垃圾对象=>被垃圾回收器回收
内存分类:栈(全局变量、局部变量(空间较小))、堆(对象(空间较大))

对象里的函数
setName:funtion(){} 调用的时候也是setName()调用
**如果对象里有特殊字符 - 空格(就是里面属性名是有特符的)
调用里面的属性要 p={} p[‘content-type’] = ‘text/json’ console.log(p[‘content-type’])
**变量名不确定时也要这样调用

函数
let a = {}
function b(){ this.xxx = ‘123’}
a.b() --不能直接调用,根本就没有
b.call(a) console.log(a.xxx) ->123 这就是js强大之处可以让任意函数成为制定任意对象的方法进行调用
回调函数
你定义的、你没有调、但最终他执行了就是回调
比如dom事件触发函数、定时器、ajax函数、生命周期函数

一般原生的js动作很多都是on开头的 比如onclick、onmouseover等 其他的框架也好jQuery等也好都是click这样的

IIFE(匿名函数自调用)
(function(){conselo.log(’…’)})()
作用:隐蔽实现、不会污染外部(全局)命名空间 (内部定义了a全局还可以定义互不影响)
( ) 是 一 个 函 数 、 ()是一个函数、 执行后返回后是一个对象


函数中的this(它的值是调用函数的当前对象)
function Person(color){
conole.log(this)
this.color = color;
this.getColor = function(){ //这两句如果不被调用是不会运行的
console.log(this)
return this.color;
}
this.setColor = function(){
console.log(this)
this.color = color
}
}

Person(‘red’) ;this ->window 没有说明的情况下默认都是指向window
let p =new Person(‘red’) this->p
p.getColor() this->p
let obj ={}
p.setColor.call(obj,‘red’) this->obj(因为前面说过任意函数可以成为某对象的调用,这就是为什么mcall改变this指向)
let test = p.setColor
test() this->window 前面说过

prototype
每个函数都有一个prototype属性,他默认指向一个Object空对象(原型对象)
(先是指向他的内部方法属性最后一层一层指向空对象,再到object.prototype.proto=null,函数里没有东西的话就是直接指向空对象)
.prototype的话直接展示一个object里面点开是函数的方法
通过.prototype.test = function(){}为其object里面添加方法(这就是为什么你创建一个实例的时候可以调用里面的test)
空的函数prototype就是空的object
原型对象中有一个属性constructor 指向函数对象
即data.prototype.constructor = data

***alt+shift+r --能够快速重命名

显式原型和隐式原型

prototype-即显式原型
每个实例对象都有一个__proto__是隐式原型
function Fn(){} let fn = new Fn()
Fn.prototype = fn.proto(牢记这个公式)
es6后面规定是可以调用隐式原型的

原型链
先说说Object的原型对象
即直接打印Object.prototype
里面常用的有toString、hasOwnproperty等
原型链的尽头是当Object.prototype.proto = null 时

***当我们去访问一个对象属性(方法也算)时,先在自身属性中查找,找到返回
如果没有找到,再沿着__proto_这条链向上查找,找到返回,如果到尽头时还是没有找到
就返回undefined

所有函数__proto__都是一样的,因为都是new function产生的(都指向Fn这样)(所以其实是用隐形传递的)
通过x.proto = y.prototype 就知道x是y的实例
最后构造函数自动继承原型对象,这就是函数能继承那个
你定义两个实例a b 分别在各自的函数内改变原来对象的值
所改变的值只会在各自里面改变,并不会改变原来的对象值,各自实例的a.proto=b.proto=该对象的.prototype
就是实例里的__proto__其实是原对象里的prototype,这个prototype最后先显示它内部的一些方法属性(.prototype.proto=object.prototype)最后会一层一层指向一个空对象的
其实这个__proto__有点像介质上一层的意思

值得注意的是不管有多少层,只要在链上可以转过去,只要一个点.就可以调用

终于弄明白了罪恶的原型链,这个地方才是关键
小题
function A(){}
A.prototype.n = 1
let b = new A()
A.prototype = {n:2,m:3}
let c = new A()
console.log(b.n,b.m,c.n,c.m) //1,undefined,2,3

原来的A->prototype是fun->{}空对象
b->实例A__proto__->指向A的prototype即空对象并为其添加一个属性
c->实例A__proto__->但这是A的prototype已经指向了{n:2,m:3}并不是空对象
每一条的指向不会变,b.m的时候一层一层往下找找不到就undefined

提一提js里的switch
for(a in abc){
switch(a){
case:“q”:
case:“w”:
text = a+1
break;
case:“e”:

}
}
用在每个值都能说的情况。。。

变量声明提升(声明的变量在定义语句之前就可以访问到,但是是undefined牢记、如果后面没有定义这个a的话没有提升就不undefined了,直接报错******)
function(){console.log(a) let a = 1 }
函数声明提升(再声明函数前可以调用,注只能是通过下面这种方式提升,这个是可以到具体函数的)
fn()
function fn(){}

执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理
函数执行上下文
在调用函数,准备执行函数体之前,创建相应的函数执行上下文对象
对局部数据进行预处理

console.log(arguments)->2,3
这个是伪数据 fn(2,3)
function fn(a1){}

执行上下文栈
let a =10
let bar = function(x){
let b = 5
console.log(x+b)
foo(x+b)
}
let foo = function(y){
let c = 5
console.log(a+c+y)
}
bar(10)
bar、foo都是定义好的在bar(10)之前并没有执行,所以foo()是可以调用的
这里说一下栈和队列
队列是一个进口一个出口(先进先出)
栈是只有一个口管进出(后进先出)想一下就OK

都执行完之后,栈底的数据肯定是window

复习题
1、
console.log(‘a’+i) ->undefined
let i = 1
foo(1)
function foo(i){
if(i===4){
return;
}
console.log(‘b’+i) ->1,2,3 //这跟后面的输出充分展示了函数提升的栈形式(后进先出)
foo(i+1)
console.log(‘c’+i)->在上面之后是3,2,1(你也可以这么理解,foo在这句前面,所以暂且不输出这一行
但是这一句是在这个函数体内的递归仍然是执行这整个函数的所以就等到前面都完事再从最后的i输出到完-完美解释栈(oh,yeah))
}
console.log(‘d’+i)->1 并没有改变i的指向就是前面定义的1,那个函数里的i只是个参数的形式,传递了个参数

2、
function a(){}
var a;
console.log(typeof a) -->‘function’ 因为先执行变量提升,再执行函数提升,覆盖了就
3、
if(!(b in window)){ var b = 1}
console.log(b) -->undefined (b in window是对的,不懂。)
4、
var c = 1
funtion c©{
console.log©
}
c(2) ->报错 函数提升了,c究竟是一个对象还是一个函数,所以就会报错
————————————————————————————————————————————————————
作用域
全局作用域(全局的)和函数作用域(函数内) ****如果想成为局部变量就一定要在函数里面,不然都会是全局变量
作用:隔离变量,不同作用域下同名变量不会有冲突
let a = 10
let b =20
function foo(x){
let a = 100
let c = 300
console.log(a,b,c,x) //100、20、300/3 ***就从局部到全局这样一层一层找
function bar(x){
let a = 1000
let d = 500
console.log(a,b,c,d,x)//1000、20、300、500、1后2
}
bar(1)
bar(2)
}
foo(3)

let obj = {
fun:function(){
console.log(fun) //这边这个fun是找不到的,因为在obj里面在作用域链
中找,要不就this.fun 要不就object.fun
}
}

————————————————————————————————————————————————————
另一大头-闭包**

如何产生闭包
当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数)时,就产生了闭包 (注意引用的变量一定是函数外部的变量,而不是全局的等变量)
function fn1(){
let a = 1
let b = 2
function fn2(){
console.log(a)
}
}
fn1()
我们只调用了fn1,并没有引用fn2,但是fn2里面引用了a这就会产生闭包,你引用那个就可以只能看到那个(在该函数的closure里)
执行闭包可以看到直接的值

产生条件
函数嵌套、内部函数引用外部函数、执行外部函数

将函数作为另一个函数的返回值
function fn1(){
let a =1
function fn2(){
a++
console.log(a)
}
return fn2
}

let f = fn1()***闭包一直没消失的根本原因就是f的存在
f()2       **如果没有闭包这一步在执行完后变量自动释放就没有a的值了,下面就会报错而不是3
f()3	   **因为闭包保存下来了a值

*****就你再执行函数的话只要闭包存在就会保留下来值继续运行

将函数作为实参传递给另一个函数调用

作用:使函数内部的变量在函数执行完后,仍然存活在内存中(延长局部变量生命周期,避免执行完就删除)
****让函数外部可以操作(读写)到函数内部的数据(变量、函数)
****平时外部看不到内部,内部可以看到外部(因为作用域链)

缺点:函数执行完后,局部变量没有释放,占用内存变长。容易造成内存泄漏
解决 让f = null 让内部函数成为垃圾对象-回收闭包

*****内存溢出与内存泄漏
内存溢出(是程序运行出现的错误,当程序运行需要内存超出剩余内存时,就会抛出内存溢出的错误)
obj ={}
for (let i=0;i<1000;i++){
obj[i] = new Array(100000)
console.log(‘lllll’)
}
这样运行之后,占用的内存太大,会使程序无法运行,在浏览器中显示内存不足打不开
在任务管理器中可以明显看到内存快速上涨又下降。

内存泄漏(占用的内存没有及时释放,内存泄漏积累多了就容易导致内存溢出)
*****常见场景(意外的全局变量、没有及时清理计时器或回调函数,闭包)
//意外的全局变量
function fn(){
a = 3
console.log(a)
}
fn()
你定义a直接写的,所以会被当成为局部变量,但是他又是全局变量,所以就会被存下来
如果是局部变量的话,用完了就会直接释放,这是很重要的一点
如果是闭包的话一定要记得及时=null释放掉内存

几个题
let name = ‘window’
let object = {
name:‘my object’,
getNameFun : function(){
return function(){
return this.name;
}
}
}
console.log(object.getNameFun()()) //window 这个地方是直接调用getNameFun里面的return里面的函数,直接执行的话是this指向window

let name = ‘window’
let object = {
name:‘my object’,
getNameFun : function(){
let that = this //用that也算闭包
return function(){
return that.name;
}
}
}

console.log(object.getNameFun()()) //object 这个地方和上面一样直接调用里面的函数,这里面的this也指向window但这地方是that
that在调用getNameFun的时候是this,所以是my object

利用that去把这个函数中的this存起来,然后在其他函数中可以用它,这是很重要的一点(因为this有可能是变化的,把某个this存起来就不会变化)


对象创建模式
Object构造函数模式
一种
let p = new Object()
p.name = ‘qqw’
p.age = ‘12’
另一种
p = {name:’’,age:’’}

原型链继承
子类型的原型为父类型的原型对象
实例对象找方法,现在原来的里面找,找不到去原型里面找
function Sup(){
this.supprop = ‘S’
}
Sup.prototype.show = function(){console.log(this.supprop)}
function sub(){
this.subprop = ‘D’
}
Sub.prototype = new Sup() //sub的实例对象指向了sup就能实现原型继承sub就可调用sup里的
Sub.prototype.show2 = function(){console.log(this.subprop)}
let sub = new Sub()
sub.show()
console.log(sub.constructor) //查看构造函数是谁 是sup 原型对象指向构造函数,这里原型对象是sup了
Sub.prototype.constructor = Sub
让子类型的原型constructor指向子类型就可以

进程
进程的一次执行,它占有一片独有的内存空间(任务管理器查看)
线程
是进程内的一个独立的执行单元,是程序执行的一个完整流程,是cpu最小的调度单元

一个程序可以有多个进程,一个进程可以有多个线程(一个进程里的线程可以直接共享)(多线程,一个进程里有一个线程就是单线程)

多线程提高cpu利用率但开销大(创建、线程切换)死锁与状态同步
单线程顺序编码简单易懂但效率低

浏览器的运行都是多线程运行的,而浏览器的进程运行有单进程也有多进程

浏览器内核

Chrome,Safari:webkit
Firefox:Gecko
IE:Trident
360,搜狗等:trident+webkit 双核双驱动
内核有很多模块组成(是一个很大的程序)(
js引擎模块:负责js程序编译运行
html/css文档解析模块:负责页面文本解析
dom与css模块:负责dom/css在内存的相关处理(因为在内存里是一个dom对象树)
布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)

这上面的是在主线程内下面是在分线程的

定时器模块:定时器管理
时间相应模块:负责事件管理
网络请求模块:负责ajax请求
。。。

定时器并不能真正保证定时
一般情况下会延迟一丁点(可以接受),遇到一个非常大的循环等情况可能延迟很长时间(不能接受)
setTimeout(function(){console.log(111)},2000)
setTimeout(function(){console.log(222)},1000)

先打印222再打印111 延迟小的先出来


js引擎执行代码的基本流程
先执行初始化代码(无论多少时间也要执行完)(包含一些特别的代码:设置定时器、绑定监听、发送ajax)
(这些特别的代码里面可能都会带回调函数,这个时候都不执行(必须在初始化代码执行完之后才能执行这些,这一步就是异步))

后面的某个时刻才会执行callback回调代码(最后执行的)
因为是单线程的一次只能执行一步,执行完一步再一步,如果遇到一个点击事件一个延时器,那就看点击在什么时候,点的慢就先出定时器,快就先出点击


事件轮询 event loop (执行完初始化函数之后,循环取出回调函数放入执行栈中执行(一个接一个))


递归的效率比较低
h5 web workers

web workers是h5提供的一个js多线程的解决方案,将大计算量的代码交由web workers运行
而不是冻结用户(就是卡顿页面)

创建一个worker
let worker = new Worker(‘worker.js’)
向分线程发送消息
let input = document.getElementById(“id”)
let num = input.value

worker.onmessage = function(e){
console.log(‘主线程接收分线程返回的数据’) 1
alert(e.data)
}
worker.postMessage(“num”)
创建监听接收worker传过来的数据函数 3

在worker.js里写
function fibonacci(n){
return n<2? 1:fibonacci(n-1)+fibonacci(n+1) //递归调用效率低
}
let onmessage = function(e){
let num = e.data
//分线程接收到主线程发送的数据 2
let result = fibonacci(num)
postMessage(result)
//分线程向主线程返回数据 4
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值