javascript高级、面试常问、必备知识点
- 1.数据类型
- 2.引用变量赋值问题
- 3. 对象和函数
- 4.函数原型与原型链
- 面试题一
- 面试题二
- 面试题一分析
- 面试题二分析
- 原型链注意点
- 5.执行上下文和执行上下文栈
- 面试题一
- 面试题二
- 面试题一分析
- 面试题二分析
- 6.作用域
- 面试题一
- 面试题二
- 面试题一分析
- 面试题二分析
- 7.闭包
- 8.线程与事件循环
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:'张三'
访问对象内部数据:.属性名 [‘属性名’]
只能使用 [‘属性名’]情况:
- 当属性名包含特殊字符: - 空格,例如:p[‘content-type’]
- 变量名不确定:
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单线程,浏览器多线程
具体事件循环面试题可以看篇文章
若有错误,请即时留言,不胜感激!