JavaScript高级教程(javascript实战进阶)


1.数据类型

分类:

  • 基本数据(值)类型
    • String:任意字符串
    • Number:任意的数字
    • boolean:true/false
    • undefined:undefined
    • null:null
  • 对象(引用)类型
  • Object:任意对象
  • Function:一种特别的对象(可以执行)
  • Array:一种特别的对象(数值下标,内部数据是有序的)

判断:

  • typeof:可以判断undefined、数组、字符串、布尔值、函数
    • 不能判断null和object
  • instanceof:可以判断对象的具体类型
  • ===:可以判断undefined、null( 因为都只有一个值)

undefined衍生至null

console.log(undefined == null) // true

2.引用变量赋值问题

(很重要!)
变量赋值是将一个变量的内容(基本数据类型的值或引用数据类型的地址值)拷贝一份给另一个变量

n个引用变量指向同一个对象,通过一个变量修改对象内部数据,其它所有变量看到的是修改后的数据
2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一对象

3. 对象和函数

对象:
对象中所有属性名都为字符串,但可以省略属性名的单引号或双引号,例如:name:'张三'
访问对象内部数据:.属性名   [‘属性名’]
只能使用 [‘属性名’]情况:

  1. 当属性名包含特殊字符: -   空格,例如:p[‘content-type’]
  2. 变量名不确定:var propName = 'myAge' var value = 18 p[propName] = value

函数中this问题:
任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
所有函数内部都有一个this
它的值是调用函数的当前对象
具体可以看着篇文章

4.函数原型与原型链

函数的prototype属性

  • 每个函数都有一个prototype属性,可称为显示原型(属性),它默认指向一个Object空对象(即称为:原型对象)
  • 原型对象中有一个属性constructor,它指向函数对象
  • 给原型对象添加属性(一般都是方法),作用:让函数的所有实例对象自动拥有原型中的属性(方法)

实例对象的__proto__属性

  • 每个实例对象都有一个__proto__,可称为隐式原型(属性)
  • 对象的隐式原型的值为其对应构造函数的显示原型的值
  • 例:Fn.prototype === fn.__proto__

原型链
访问一个对象的属性时

  • 先在自身属性中查找,找到返回;
  • 如果没有,再沿着__proto__这条链上查找,找到返回;
  • 如果都没有找到,则返回undefined

别名:隐式原型链
作用:查找对象的属性(方法)

在这里插入图片描述

注意点:

  • 所有函数对象的__proto__都是一样的,都指向Function的prototype(都是new Function出来的)
  • 函数的显示原型指向的对象默认是空的Object实例对象,但Object不满足,例:Object.prototype instanceof Object // false(因为A instanceof B,是判断B的显示原型是否在A的原型链上,而Object.prototype指向上图中Object的原型对象,其已经是原型链的尽头了)
  • 所有函数都是Function的实例(包含Function)
  • Object的原型对象是原型链的尽头

面试题一

function A(){

}
A.prototype.n = 1
var b = new A()
A.prototype = {
    n:2,
    m:3
}
var c = new A()
console.log(b.n,b.m,c.n,c.m)

面试题二

function F (){}
Object.prototype.a = function(){
    console.log('66')
}
Function.prototype.b = function(){
    console.log('88')
}
var f = new F()
f.a()
f.b()
F.a()
F.b()

在这里插入图片描述

面试题一分析

输出:1 undefined 2 3
分析:b.n可通过b的隐式原型链查找,而b.m查不到,由于把一个新对象赋值给了A函数的显示原型,因此c的隐式原型指向会发生更改,指向A函数新的显示原型
在这里插入图片描述

面试题二分析

输出:66 报错 66 88
分析:查找f.a时,本身没有,沿着其隐式原型链可查到Object函数的原型对象上,查找成功,执行a函数,但无法查询到f.b,因为此时已是原型链的尽头了,因返回undefined,此执行f.b()报错。同理F.a和F.b都可以通过隐式原型链查询到。
(以下原型对象没有区分名字,且忽略了地址)在这里插入图片描述

原型链注意点

  • 通过图中标记一、二、三,可以得知每个函数都有显示原型和隐式原型(都是new Function()出来的,其都指向Function的显示原型)
  • 标记一可知,Function是特例,其显示原型和隐式原型(也是new Function()出来的)指向相同
  • 标记二可知,Object函数对象也是new Function()出来的,其隐式原型指向Function的显示原型
  • 标记三可知,任何函数其隐式原型都指向Function的显示原型

5.执行上下文和执行上下文栈

变量声明提升

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

变量提升先于函数提升

函数声明提升

  • 通过function声明的函数,在之前就可以直接调用

全局执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理

  • var 定义的全局变量,undefined,添加为window的属性
  • function声明的全局函数,添加为window的方法
  • this赋值为window

开始执行全局代码

函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
对局部数据进行预处理
-形参变量赋值为实参,添加为执行上下文的属性

  • arguments赋值实参列表,添加为执行上下文的属性
  • var定义的局部变量,undefined,添加为执行上下文的属性
  • function声明的函数,添加为执行上下文的方法
  • this赋值为调用函数的对象

开始执行函数体代码

面试题一

var a = 3
function fn(){
    console.log(a)
    var a = 4
}
fn()

面试题二

var c =1
function c(c){
    console.log(c)
    var c = 3
}
c(2)

面试题一分析

输出:undefined
分析:在函数体内,使用var定义的变量,声明为局部变量,又由于存在变量提升,因此源代码相当于以下代码

var a = 3
function fn(){
	var a
    console.log(a)
    a = 4
}
fn()

面试题二分析

输出:报错
分析:var定义的变量和函数声明式创建的函数会提升,因此源代码相当于以下代码,c为变量,无法执行c(2)

var c
function c(c){
    console.log(c)
    var c = 3
}
c = 1
c(2)

6.作用域

面试题一

var x = 10
function fn(){
    console.log(x)
}
function show(f){
    var x = 20
    fn()
}
show(fn)

面试题二

 var fn = function(){
     console.log(fn)
 }
 fn()

 var obj = {
     fn2:function(){
         console.log(fn2)
     }
 }
 obj.fn2()

面试题一分析

输出:10
分析:作用域在代码编写完毕时就已经确定了。执行fn(),找到的x为全局作用域x。

面试题二分析

输出:fn函数  报错
分析:第一个输出很好理解。第二个,当执行obj.fn2(),首先在函数内部作用域找fn2,找不到,则去外部全局作用域中找fn2,找不到。若想找到,应该使用this.fn2。这里要区分以下三种输出。

 var obj = {
     fn2:function(){
         console.log(fn2)
         console.log(this.fn2)
         console.log(window.fn2)
     }
 }
 obj.fn2()

7.闭包

//利用闭包实现循环遍历加监听
for(var i = 0,length=btns.length;i<length;i++){
    (function(i){
        var btn = btns[i]
        btn.onclick = function(){
            alert('第'+(i+1)+'个')
        }
    })(i)
}

如何产生闭包?

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

闭包是什么? 通过chrome浏览器,在debug下查看

  • 理解一:闭包是嵌套的内部函数(绝大部分人)
  • 理解二:闭包是包含被引用变量(或函数)的对象(极少数人)
    注意:闭包存在于嵌套的内部函数中

产生闭包的条件?

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
  • 执行外部函数,也即要执行内部函数定义就会产生闭包(不用执行内部函数)

常见的闭包?

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

闭包的作用?

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

闭包的产生和死亡?

  • 产生:在嵌套内部函数定义执行完时就产生了(不是调用,有函数提升)
  • 死亡:在嵌套的内部函数成为垃圾对象时
    注意:产生闭包的个数,看外部函数执行的次数

闭包的缺点?

  • 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
  • 容易造成内存泄露
    解决:能不用闭包就不用,及时释放

闭包应用:自定义JS模块

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

内存溢出与内存泄露

内存溢出

  • 一种程序运行出现的错误
    当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误

内存泄露

  • 占用的内存没有及时释放
  • 内存泄露积累多了就容易导致内存溢出
  • 常见的内存泄露:意外的全局变量、没有及时清理的计时器或回调函数、闭包

8.线程与事件循环

进程与线程

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

JS单线程,浏览器多线程

具体事件循环面试题可以看篇文章

在这里插入图片描述

若有错误,请即时留言,不胜感激!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值