变量类型:根据存储方式区分
值类型(变量存放值)
let a = 100
let b = a
a = 200
console.log(b) // 100
引用类型(变量存放地址):
对象,数组,函数
let a = {age: 20}
let b = a // b和a都指向同一个地址,因此支持动态添加属性
b.age = 21
console.log(a.age) // 21
typeof运算符
规律:只能区分值类型和函数(引用类型数组和函数不能判断)
判断值 | 输出结果 |
---|---|
undefined |
undefined |
ywh |
string |
41 |
number |
true |
boolean |
{} , [] , null
|
object |
console.log |
function |
强制类型转换
字符串拼接
let a = 100 + 10 // 110
let b = 100 + '10' // '10010'
==
运算符
// 以下判断返回结果都为true
100 == '100'
0 == ''
null == undefined
// 除了这个以外其余都用“===”
if (obj.a == null) {
// ...
}
// 建议都使用`===`或参考jQuery源码推荐写法
if
语句
// 注意当a未定义时,if (a == null) 或 if (a == undefined)会报错
let a = true
let b = 100
let c = ''
if (a) {
// true
}
if (b) {
// true
}
if (c) {
// false
}
逻辑运算符
10 && 0 // 0
'' || 'abc' // 'abc'
!window.abc // true
// 判断一个变量会被当做true还是false
let a = 100
!!a // 反反
安全赋值(避免覆盖)
var a = a || {} // a的值存在则不允许定义
内置函数
- Object
- Array
- Boolean
- Number
- String
- Function
- Date
- RegExp
- Error
- Math
原型和原型链
构造函数
-
var a = {}
是var a = new Object()
的语法糖; -
var a = []
是var a = new Array()
的语法糖; -
function Foo(){...}
是var Foo = new Function(...)
语法糖; - 可使用
a instanceof Array
判断一个函数是否是一个变量的构造函数(如区分数组和对象)。
// 注意构造函数首字母要用大写
function Foo(name, age) {
this.name = name
this.age = age
// return this // 默认返回this
}
let f = new Foo('ywh', 18) // 创建一个实例
原型规则
- 所有引用类型(数组,对象,函数)都具有共有的对象特性,即可自由扩展属性(
null
除外); - 所有的引用类型都有一个
__proto__
属性(对象),即隐式原型,存放该类型共有属性的地址(指向构造函数的prototype
,即(new Object).__proto__ === Object.prototype
); - 所有的函数都有一个
prototype
属性(对象),即显式原型; - 当试图访问对象的某个属性,会先从对象本身查找,不存在则会从
__proto__
沿着原型链依次查找; -
f instanceof Foo
的原理也是沿着原型链一层层往上判断,看可否达到Foo.protype
,再试着判断f instance of Object
。
function Foo(name, age) {
this.name = name
this.age = age
}
Foo.prototype.alertName = function () {
alert(this.name)
}
let f = new Foo('ywh')
f.printName = function () {
console.log(this.name)
}
f.printName() // 自身属性
f.alertName() // 原型属性
f.toString() // 原型的原型的属性
// new 创建对象的过程
// 1. 创建一个新对象
// 2. this指向这个新对象
// 3. 执行代码,对this赋值
// 4. 返回this
循环对象自身的属性:
var item
for (item in f) {
if (f.hasOwnProperty(item))
console.log(item)
}
// 高级浏览器已经在“for...in...”中屏蔽来自原型的属性,但还是建议加入判断
原型链继承的例子:
function Animal() {
this.eat = function () {
congole.log("animal eat")
}
}
function Dog() {
this.bark = function () {
console.log("dog bark")
}
}
Dog.prototype = new Animal()
var dog = new Dog()
实例:封装DOM查询
function Elem(id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val) {
let elem = this.elem
if (val) {
elem.innerHTML = val
return this
}
else {
return elem.innerHTML
}
}
Elem.prototype.on = function (type, fn) {
let elem = this.elem
elem.addEventListener(type, fn)
}
let div1 = new Elem('detail-page')
div1.html("<p>Hello World</p>").on("click", function(){
alert("click")
})
执行上下文
- 范围:一段<script>或者一个函数;
- 全局:变量定义、函数声明
- 函数:变量定义、函数声明、this、arguments
console.log(a) // undefined
var a = 100 // 把var a提升到最上面,此处剩余a = 100,所以console.log时a还未被赋值
fn("ywh") // "ywh" 20
function fn(name) { // 被提升到“fn("ywh")”之前
age = 20
console.log(name, age)
var age // 被提升到“age = 20”之前
}
this
-
this
是call
的第一个参数,在执行时才确定值,定义时无法确认;- 作为构造函数执行;
- 作为普通函数执行;
- call,apply,bind
let 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
作用域
- ES6之前只有函数作用域与全局作用域,没有块级作用域;
if (true) { // 此处没有隔离作用域,是全局声明
var name = "ywh"
}
console.log(name)
var a = 100
funtion fn() {
var a = 200
console.log("fn", a)
}
console.log("global", a)
fn()
作用域链(树)
var a = 100
function fn1() {
var b = 200
function fn2() {
var c = 300
console.log(a) // 当前作用域没有定义的变量,即“自由变量”,会从当前作用域向上查找
console.log(b)
console.log(c)
}
fn2()
}
fn1()
闭包
使用场景
- 函数作为返回值
- 函数作为参数传递
主要用于封装变量,收敛权限
function fn1() {
var a = 100
return function() {
console.log(a) // a是自由变量,向父级作用域查找,因此是100
}
}
// 函数作为返回值
var f1 = fn1()
var a = 200
f1() // 100
// 函数作为参数传递
function fn2(fn) {
var a = 200
fn().call()
}
fn2(fn1)
实例:利用闭包保护变量
// 错误
var i, a
for (i = 0; i < 10; i ++ ) {
a = document.createElement("a")
a.innerHTML = i + "<br>"
a.addEventListener("click", function(e) {
e.preventDefault()
alert(i)
}) // 循环执行完才点击,i是自由变量,会从父作用域查找,此时i === 9,因此点击任何一个都会输出9
document.body.appendChild(a)
}
// 正确
var i
for (i = 0; i < 10; i ++ ) {
(function(i) {
var a = document.createElement("a")
a.innerHTML = i + "<br>"
a.addEventListener("click", function(e) {
e.preventDefault()
alert(i) // 此处的i使用的是外部变量,因此构成闭包,值会一直保存在函数作用域不会被修改
})
document.body.appendChild(a)
})(i) // 封装成一个立即执行闭包函数
}
实例:使用闭包收敛权限
function isFirstLoad() {
var _list = [] // 一直保存在内存中
return funciton(id) {
if (_list.indexOf(id) >= 0) {
return false
}
else {
_list.push(id)
return true
}
}
}
var firstload = isFirstLoad()
firstload(10) // true
firstload(10) // false
firstload(20) // true
异步和单线程
JS是单线程执行,因此很多场景下要使用异步。
同步:代码顺序执行,中间可能会阻塞
console.log(100)
alert(200) // 同步执行,点确认前会阻塞
console.log(300)
异步:不考虑执行结果(或通过回调获取执行结果),调用立即返回;
使用异步的场景:在可能发生等待、且等待过程中不能阻塞(像alert一样):
-
setTimeout
,setInterval
; - ajax请求,动态
<img>
加载; - 事件绑定。
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
console.log(4)
}, 1000)
console.log(5)
// 1, 3, 5, 2, 4
// $.get("https://www.baidu.com", function(data){console.log(data)})
Event-Loop(事件轮询):
- 顺序执行所有同步函数;
-
setTimeout
执行的异步函数会在等待完成后依次暂存在异步队列(单线程); - 轮询异步队列,依次取出函数执行。
其他API
日期
Date.now()
var dt = new Date()
dt.getTime()
dt.getFullYear()
dt.getMonth()
dt.getDate()
dt.getHours()
dt.getMinutes()
dt.getSeconds()
数学
Math.random() // 获取随机数
数组
// 遍历所有元素
var arr = [1, 2, 3]
arr.forEach(function(item, index) {
console.log(index, item)
})
// 判断是否所有元素都符合条件,some判断是否至少一个元素符合条件
var arr = [1, 2, 3]
var result = arr.every(function(item, index) {
if (item < 4) {
return true
}
})
console.log(result)
// 排序
var arr = [1, 4, 2, 3]
var arr2 = arr.sort(function(a, b) {
return a - b
})
console.log(arr2)
map // 对元素重新组装生成新数组
filter // 过滤符合条件的元素
// ...
对象
var obj = {
x: 100, y: 200, z: 300
}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key])
}
}
回答以下问题
- JS中使用typeof能得到的哪些类型?
- 何时使用
==
,何时使用===
? - JS中有哪些内置函数?
- JS变量按照存储方式区分为哪些类型,并描述其特点。
- 如何理解JSON?
- 如何准确判断一个变量是数组类型?
- 写一个原型链继承的例子。
- 描述new一个对象的过程。
- zepto(值得阅读,或其他框架)源码中如何使用原型链?
- 如何理解变量提升?
- 说明this几种不同的使用场景。
- 创建10个<a>标签,点击时弹出来对应的序号。
- 如何理解作用域?
- 实际开发中闭包的作用?
- 同步和异步的区别是什么?分别举一个同步和异步的例子。
-
setTimeout
函数的用法。 - 前端使用异步的场景有哪些?
- 获取2017-06-10格式的日期。
- 获取随机数,要求是长度一致的字符串格式。
- 写一个能遍历对象和数组的通用forEach函数。
网友评论