这个是学习javascript面试视频整理学习的笔记,感觉自己一直在用es6写代码,反而对基础的东西又所欠缺,所以就学习了这个视频,后续还会整理更多打出来。
JS基础
typeof 运算符可以算出来的类型?
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log // function
typeof
只能判断值类型,无法具体判断具体的引用类型。
发生强制类型转换的地方
- 字符串拼接
- == 运算符
- if 语句
- 逻辑运算
1.字符串拼接
var a = 100+'10' //'10010'
2. == 运算符
100 == '100' // true
0 == '' // true
null == undefined // true
他们转换为false 或者转为true
3.if 语句
var a = true
if(a){
//...执行
}
var b = 100
if(b){
//...执行
}
var c = ''
if(c){
//...不执行,会转为false
}
4.逻辑运算符
console.log(10 && 0) //0
console.log('' || 'abc') // 'abc'
console.log(!window.abc) // true
判断一个变量会被当做true
还是false
?
var a =100
console.log(!!a)
何时使用 ===
和 ==
?
if(obj.a == null){
//这里相当于 obj.a === null || obj.a === undefined
//这是Jquery源码中推荐的写法
}
其他地方通通用===
,而不用==
,因为它会自动类型转换
JS中的内置函数
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
JS按存储方式区分变量类型
值类型
var a = 10
var b = a
a = 11
console.log(b); // 10
值类型不占用空间,每一次赋值都是深度拷贝
引用类型
var obj1 = {x:100}
var obj2 = obj1
obj1.x = 200
console.log(obje2.x)
引用类型赋值的时候,只复制了对象的引用,多个引用指向一个对象,这样可以节省内存。
如何理解JSON
JSON
现在就是一个JS
对象,常用的API
:
JSON.stringify({a:20,b:20})
JSON.parse('{'a':10,'b':10}')
原型和原型链
构造函数
//构造函数的一个特点就是 函数首字母大写
function Foo(name,age){
this.name = name
this.age = age
this.class = 'class-1'
//return this //默认有这一行
}
var f = new Foo('zhangsan',20);
//var f1 = new Foo('lisi',22); //创建过个对象
在new
一个对象的时候,会首先创建一个this
的空对象,然后进行赋值,最后默认会返回this
构造函数--扩展
-
var a = {}
其实是var a = new Object()
的语法糖 -
var a = []
其实是var a = new Array()
的语法糖 -
function Foo(){...}
其实是var Foo = new Function(...)
- 使用
instanceof
判断一个函数是否是一个变量的构造函数
原型规则和示例
5条原型规则:原型规则是学习原型链的基础
- 所有的引用类型(数组,对象,函数),都具有对象特性,即可自由扩展属性(除了
null
以外) - 所有的引用类型(数组,对象,函数),都有一个
__proto__
属性,属性值是一个普通的对象,隐式原型var obj = {a:10} undefined console.log(obj.__proto__)
- 所有的函数,都有一个
prototype
属性,属性值也是一个普通的对象,显示类型function fun(){} console.log(fn.prototype)
- 所有的引用类型(数组,对象,函数),
__proto__
属性值指向它的构造函数的prototype
属性值var obj = {}; console.log(obj.__proto__ === Ojbect.prototype)
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的
__proto__
(即它的构造函数prototype
)中寻找
这里的//构造函数 function Foo(name){ this.name = name } Foo.prototype.alertName = function(){ alert(this.name) } //创建示例 var f = new Foo('zhangsan'); f.printName = function(){ console.log(this.name) } //测试 f.printName() f.alertName()//找不到,调用构造函数prototype的
this
无论是原型链中的还是对象中的,都是指向对象本身的
循环便利对象自身的属性
var item
for(item in f){
//for in 便利的时候会便利原型中的属性,所以我们需要判断它是否属性当前对象的
if(f.hasOwnProperty(item)){
console.log(item)
}
}
原型链
这里看下面的f.toString()函数
,我们并没有在构造函数的prototype中定义,那么应该去哪里找呢?
根据上面定义的第五条规则:
**当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__
(即它的构造函数prototype
)中寻找。 **
我们没有在f.__proto__
这个对象中找到,那么我们就去它的__proto__
中去找,也就是
f.__proto__.__proto__
中去找,这就是原型链。
//构造函数
function Foo(name){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name)
}
//创建示例
var f = new Foo('zhangsan');
f.printName = function(){
console.log(this.name)
}
//测试
f.printName()
f.alertName()//找不到,调用构造函数prototype的
f.toString() //
我们看下面的图:

instanceof
判断一个对象是否数一个构造函数,
f instanceof Foo
的判断逻辑是:
f
的__proto__
一层一层往上,能否对应到Foo.prototype
我们在判断 f instanceof Object
,同样也是正确的
如何准确判断一个变量是数组类型
```
var arr = [];
arr instanceof Array //true
typeof arr //object typeof判断引用类型除了函数,都是object,无法区分数组
```
写一个原型链继承的例子
//动物
function Animal(){
this.eat = function(){
console.log('animal eat');
}
}
//狗
function Dog(){
this.bark = function(){
console.log('dog bark');
}
}
Dog.prototype = new Animal();
//哈士奇
var hashiqi = new Dog();
//面试的时候写一个实战的例子,这个比较`low`
描述new 一个对象的过程
1.创建一个新对象
2.this
指向这个新对象
3.执行代码,即对this
赋值
4.返回this
作用域和闭包
常见面试题:
- 说一下对变量提升的理解
- 说明this 几种不同的使用场景
- 创建10个<a>标签,点击的时候弹出对应的序号
- 如何理解作用域
- 实际开发中闭包的应用
知识点
执行上下文--变量提升
范围:一段<script>或者一个函数
全局:变量定义,函数声明
函数:变量定义,函数声明,this
,arguments
注意:函数声明和函数表达式的区别,函数表达式并不会提前声明哦。
什么意思呢?你知道javaScript
是解释型语言,它不会被编译,而是在浏览器里解释执行。所以它在执行之前会把全局里面的变量定义,函数声明,函数里面的变量定义,函数声明,this
,arguments
摘出来,先把他们都声明了,进行占位。
console.log(a) //undefined
var a = 100
fn('zhangsan') //'zhangsan' 20
function fun(name){
age = 20
console.log(name,age);
var age
}
所以看上面的代码:上面的代码中,在执行之前,我们会把var a
和function fn
先拿出来占位,因为是声明,所以会先把他们的值设定为undefined
.这个就是变量提升。
函数上下文和全局的一样,在函数上下文执行之前,同样也是需要先把变量定义提前声明,拿出来占位了,所以在赋值的时候age=20
并没有报错,因为它已经执行之前就声明了。
函数声明和函数表达式
fn()//不会报错
function fn(){
}
fn1()//会报错
var fn1 = function(){
}
函数声明会变量提升,提前注册,而函数表达式并不会,所以会报错
this
记住一句话: this
要在执行时才能确认之,定义时无法确认。
var a = {
name:"A,
fn:function (){
console.log(this.name)
}
}
a.fn() //this === a
a.fn.call({name:'B'}) // this === {name:'B'}
var fn1 = a.fn
fn1() //this === window
javaScript
是解释型语言,它不会进行编译,所以我们在上面定义的对象a
,它仅仅只是并定义了,并没有执行,而在执行的时候,才会有它具体的上下文。
在上面列出的代码中有它当时的具体的执行环境,造成了this
的指向不同。
总结一下:
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind
作用域
1.没有块级作用域,所有后来有了let
和const
if(true){ var name = "zhangsan" } console.log(name)
因为函数提升的原因,所以我们可以直接打印出来。所以没有块级作用域
2.只有函数和全局作用域
var a = 100 function fn(){ var a = 200 console.log('fn',a) } console.log('global',a) fn()
在第一行中var a = 100
,声明后,它是一个全局的变量,任何地方都可以修改它。而如果你不想你声明的变量被被人修改,那么就在你的函数里声明,函数外面是无法修改你的变量的。
作用域链
什么叫自由变量?
var a = 100;
function fn(){
var b = 200
//当前作用域没有定义的变量,即“自由变量”
console.log(a);
console.log(b);
}
fn();
当我们在函数中打印console.log(a)
的时候,在当前函数作用域中并没有定义a
,那么它会去函数定义的父级作用域中寻找,当前函数的父级作用域就是全局,所以寻找到的a
为100
.
var a = 100
function F1(){
var b = 200
function F2(){
var c = 300
console.log(a);
console.log(b);
console.log(c);
}
F2()
}
F1()
在上面的代码中,当我们在函数F2
中执行打印console.log(a);
的时候,因为当前函数作用域中并没有定义a
,所以就去它定义的父级作用域中寻找,也就是F1
,然后发现F1
中也没有,那么就继续往上寻找,直到找到。
这就是作用域链。
闭包
看一段代码:
function F1(){
var a = 100
//返回一个函数 函数作为返回值
return function(){
console.log(a) //a 自由变量,向父级作用域去寻找 ---函数定义的地方
}
}
var f1 = F1()
var a = 200
f1()
在ES5
中,由于没有块级作用域,在外部的定义的变量都会变成全局变量,造成了变量的污染,而函数作用域的关系,我们可以通过函数作用域来模拟块级作用域,通过在函数中定义变量,然后在函数中定义函数来访问函数中的变量,可以达到我们的封装变量的目的。这个就是闭包。在上面的代码中,我们定义了一个函数,然后在它的里面定义了一个变量,然后返回一个函数,返回的函数里访问F1
中的变量。这样就达到了访问函数中的变量的目的。
闭包的使用场景:
- 函数作为返回值(例如上面的代码)
- 函数作为参数传递
function F1(){
var a = 100
return function(){
console.log(a)
}
}
var f1 = F1()
function F2(fn){
var a = 300
fn();
}
F2(f1); //100
创建10个<a>标签,点击的时候弹出来对应的序号
这是个错误的写法:
var i,a
for(i=0;i<10;i++){
}
这是正确的写法
var i
for(i=0;i<10;i++){
(function(i){
a = document.createElement('a')
a.innerHTML = i +'<br>'
a.addEventListener('click',function(){
e.preventDafault()
alert(i)
});
document.body.appendChild(a)
})(i)
}
网友评论