知识点一:变量的类型和计算
变量类型
JS变量最基本的分类就是值类型和引用类型,两者有何区别呢,可以通过例子看出来。
以下是值类型的一个例子
var a = 100
var b = a
a = 200
console.log(b)
以下是引用类型的一个例子
var a = {age:20}
var b = a
b.age = 21
console.log(a.age)
typeof
可以知道一个值类型是什么类型,而对于引用类型,它就无能为力了。但是它可以将引用类型区分出function
,为什么 ———— 因为function
相对于其他引用类型(如对象、数组)来说,具有非常特殊的意义,JS 中的函数非常重要,接下来的原型、作用域都会深入讲解函数。
JS 中的某些表现,就已经体现了函数的特殊意义,例如:对象和数组,JS中没有内置的(不考虑 JS-WEB-API),而函数却内置了很多,例如 Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
。这些函数 JS 本身就有,要是没有它们,就没法愉快的写 JS 代码了。因为他们是基础数据类型的构造函数(后面会讲解)
typeof
可以区分类型有number
string
boolean
undefined
(值类型) function
object
(引用类型)
// 特例
typeof null // object 因为 null 也是引用类型。null 就相当于引用类型中的 undefined
那么针对第二个例子,如何将a
的内容复制给b
,并且保证b
的修改不会影响到a
呢?那就需要深度复制,意思就是对a
的属性进行递归遍历,再依次复制,这块我们会放在后面专门讲解。
变量计算
组简单的计算,就是数字的加减乘除、字符串的拼接和替换,这个太简单了,这里不提了。但是 JS 在值类型的运算过程中,特别需要注意和利用强制类型转换这一特性,有以下场景:
- 字符串拼接
==
- 逻辑运算(
if
!
||
&&
)
字符串拼接最常见的错误如下,特别要注意。如何规避呢 ———— 对进行计算的变量通过typeof
来判断类型 ———— 太麻烦?编码本身就是一个体力活!
var a = 100 + 10 // 110
var b = 100 + '10' // '10010'
接下来,==
也会进行强制类型转换,如
100 == '100' // true
0 == '' // true
null == undefined // true
针对100 == '100'
就是和拼接字符串一样的类型转换,而针对下面两个例子,就是一个逻辑运算上的强制类型转换(马上会讲解)。所以,要求你写 JS 代码时,所有的地方都要使用===
而不能使用==
,但是阅读 jquery 源码后我发现一个特例,就是obj.a == null
,使用很简洁。
最后,逻辑运算中的强制类型转换,先以if
为例说明
var a = true
if (a) {
// ....
}
var b = 100
if (b) {
// ....
}
var c = ''
if (c) {
// ....
}
所有经过if
判断的变量,都会进行逻辑运算的强制类型转换,转换为true
或者false
。
console.log(10 && 0) // 0
console.log('' || 'abc') // 'abc'
console.log(!window.abc) // true
// 判断一个变量会被当做 true 还是 false
var a = 100
console.log(!!a)
日常开发中,以下变量会被转换为false
- 0
- NaN (NaN用来表示最终结果并非数字。)
- ‘’
- null
- undefined
- false 本身
除了以上几个,其他的都会被转换为true
。除了if
之外,!
||
&&
这三个运算符也会进行同样的转换,跟if
是一个道理。因此,如何快速判断一个变量将会被if
转换为什么呢?————!!a
答题
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
何时使用===
何时使用==
首先你得明白两者的区别。==
会先试图类型转换,然后再比较,而===
不会类型转换,直接比较。如下例子:
1 == '1' // true
1 === '1' // false
0 == false // true
0 === false // false
null == undefined // true
null === undefined // false
根据 jQuery 源码中的写法,只推荐在一个地方用==
,其他地方都必须用===
。这个用==
的地方就是:
if (obj.a == null) { // 这里相当于 obj.a === null || obj.a === undefined ,简写形式
}
编程是需要绝对严谨的态度,我们只在这一个地方让它进行类型转换,来简化我们的写法,因为这个场景非常简单和固定。而其他场景下,我们都必须使用===
,除非有特殊的业务需要。
JS中有哪些内置函数 —— 数据封装类对象
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
JS变量按照存储方式区分为哪些类型,并描述其特点
- 值类型
undefined
string
number
boolean
- 引用类型
object
function
最后补充一点,在 JS 中,所有的引用类型都可以自由设置属性
var obj = {}
obj.a = 100
var arr = []
arr.a = 100
function fn() {}
fn.a = 100
如何理解JSON
这个问题,很容易被一些初学者误答。其实,JSON 是什么?从 JS 角度回答,太简单了,console.log(JSON)
得到JSON
只是一个对象,有parse
和stringify
两个方法,使用也非常简单
JSON.stringify({a:10, b:20})
JOSN.parse('{"a":10,"b":20}')
知识点二:原型和原型链
JS 是基于原型的语言,原型理解起来非常简单。任何长存不会被遗弃和提到的东西,都是最简单的东西。
构造函数
所有的 JS 入门教程都会有类似这样的例子
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 Foo
创建出来一个f
对象,对象有name
age
class
三个属性,这样我们就称Foo
是f
的构造函数。构造函数这个概念在高级语言中都存在,它就像一个模板一样,可以创建出若干个示例。
函数执行的时候,如果前面带有new
,那么函数内部的this
在执行时就完全不一样了,不带new
的情况我们下一章节会讲到。带new
执行时,函数中的this
就会变成一个空对象,让程序为其属性赋值,然后最终返回。return this
是默认执行的,如何验证?———— 你可以最后加一个return {x:10}
试一下。返回之后,f
就被赋值成了这个新对象,这样就创建完成了。
构造函数 - 扩展
var a = {}
其实是var a = new Object()
的语法糖var a = []
其实是var a = new Array()
的语法糖function Foo(){...}
其实是var Foo = new Function(...)
的语法糖
大家看到以上几点,明白我要表达的意思了吗?
如何判断一个函数是否是一个变量的构造函数呢 ———— 使用instanceof
,原理接下来就会讲到。
几个要点
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了
null
意外) - 所有的引用类型(数组、对象、函数),都有一个
__proto__
(隐式原型)属性,属性值是一个普通的对象 - 所有的函数,都有一个
prototype
属性,属性值也是一个普通的对象 - 所有的引用类型(数组、对象、函数),
__proto__
属性值指向它的构造函数的prototype
属性值
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
console.log(fn.prototype)
console.log(obj.__proto__ === Object.prototype)
原型
我们先将一开始的示例做一下改动,然后看一下执行的效果
// 构造函数
function Foo(name, age) {
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()
执行printName
时很好理解,但是执行alertName
时发生了什么?这里再记住一个重点 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__
(即它的构造函数的prototype
)中寻找
那么如何判断一个这个属性是不是对象本身的属性呢?使用hasOwnProperty
,常用的地方是遍历一个对象的时候
var item
for (item in f) {
// 高级浏览器已经在 for in 中屏蔽了来自原型的属性,但是这里建议大家还是加上这个判断,保证程序的健壮性
if (f.hasOwnProperty(item)) {
console.log(item)
}
}
原型链
还是接着上面的示例,执行f.toString()
时,又发生了什么?因为f
本身没有toString()
,并且f.__proto__
(即Foo.prototype
)中也没有toString
。这个问题还是得拿出刚才那句话————当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__
(即它的构造函数的prototype
)中寻找
如果在f.__proto__
中没有找到toString
,那么就继续去f.__proto__.__proto__
中寻找,因为f.__proto__
就是一个普通的对象而已嘛!
这样一直往上找,你会发现是一个链式的结构,所以叫做“原型链”。直到找到最上层都没有找到,那么就宣告失败,返回undefined
。最上层是什么 ———— Object.prototype.__proto__ === null
原型链中的this
所有的从原型或者更高级的原型中得到、执行的方法,其中的this
在执行时,就指向了当前这个触发事件执行的对象。因此printName
和alertName
中的this
都是f
。
instanceof
开始介绍的instanceof
这里再讲一下原理。如果要计算f instanceof Foo
是不是正确,就要判断f
的原型一层一层往上,能否对应到Foo.prototype
。同理,如果要计算f instanceof Object
是不是正确,就要判断f
的原型一层一层往上,能否对应到Object.prototype
知识点三:作用域和闭包
执行上下文
先看下面的例子,你可能会对结果比较差异。当然,我不建议在实际开发中通过这种方式来炫技,我们这里演示纯粹是为了讲解知识点做一个铺垫。
console.log(a) // undefined
var a = 100
fn('zhangsan') // 'zhangsan' 20
function fn(name) {
age = 20
console.log(name, age)
var age
}
在一段 JS 脚本(即一个<script>
标签中)执行之前,会先创建一个全局执行上下文环境,先把代码中即将执行的(内部函数的不算,因为你不知道函数何时执行)变量、函数声明(和“函数表达式”的区别)都拿出来。变量先暂时赋值为undefined
,函数则先声明好可使用。这一步做完了,然后再开始正式执行程序。再次强调,这是在代码执行之前才开始的工作。
另外,一个函数在执行之前,也会创建一个函数执行上下文环境,跟全局上下文差不多,不过函数执行上线文中会多出this
arguments
和函数的参数。参数和arguments
好理解,这里的this
咱们需要专门讲解。
总结一下
- 范围:一段
<script>
或者一个函数 - 全局:变量定义,函数声明
- 函数:变量定义,函数声明,this,arguments
this
先搞明白一个很重要的概念 ———— 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
this
执行会有不同,主要集中在这几个场景中
-
作为构造函数执行
-
作为对象属性执行
-
作为普通函数执行
-
用于
call
apply
bind
-
1.构造函数
function Foo(name){
//var this = {}
this.name=name
//return this
}
var f = new Foo('zhangsan) -
2.对象属性
var obj = {
name:‘A’,
printName:function(){
console.log(this.name)
}
}
obj.printName() -
3.普通函数执行
function fn(){
console.log(this)
}
fn() //window -
4.
call
apply
bind
function fn1(name,age){
console.log(name)
console.log(this)
}
fn1.call({x:100},‘zhangsan’,20)
fn1.apply({x:100},[‘zhangsan’,20])
var fn2 = function(name,age){
console.log(name)
console.log(this)
}.bind({y:300})
fn2.(‘zhangsan’,300)
前两种情况咱们之前都介绍过了,这里只是统一的提出来,汇总一下,不再详细讲了。这里主要说第三种
function fn() {
console.log(this)
}
fn() // window
fn.call({a:100}) // {a:100} 和 call 同理的还有 apply bind
作用域
自由变量
作用域链,(自由变量的查找)
闭包的两个场景
作为有 JS 基础的同学,你应该了解 JS 没有块级作用域。例如
if (true) {
var name = 'zhangsan'
}
console.log(name)
从上面的例子可以体会到作用域的概念,作用域就是一个独立的地盘,让变量不会外泄、暴露出去。上面的name
就被暴露出去了,因此,JS 没有块级作用域,只有全局作用域和函数作用域。
var a = 100
function fn() {
var a = 200
console.log('fn', a)
}
console.log('global', a)
fn()
全局作用域就是最外层的作用域,如果我们写了很多行 JS 代码,变量定义都没有用函数包括,那么他们就全部都在全局作用域中。这样的坏处就是很容易装车。
// 张三写的代码中
var data = {a:100}
// 李四写的代码中
var data = {x:true}
这就是为何 jquery zepto 等库的源码,所有的代码都会放在(function(){....})()
中。因为放在里面的所有变量,都不会被外泄和暴露,不会污染到外面,不会对其他的库或者 JS 脚本造成影响。这是函数作用域的一个体现。
作用域链
首先认识一下什么叫做自由变量。如下代码中,console.log(a)
要得到a
变量,但是在当前的作用域中没有定义a
(可对比一下b
)。当前作用域没有定义的变量,这成为自由变量。自由变量如何得到 ———— 向父级作用域寻找。
var a = 100
function fn() {
var b = 200
console.log(a)
console.log(b)
}
fn()
如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a)
console.log(b)
console.log(c)
}
F2()
}
F1()
闭包
直接看一个例子
function F1() {
var a = 100
return function () {
console.log(a) //a是自由变量 函数 - 定义的时候的父作用域
}
}
var f1 = F1()
var a = 200
f1() //100
自由变量将从作用域链中去寻找,但是依据的是函数定义时的作用域链,而不是函数执行时,以上这个例子就是闭包。闭包主要有两个应用场景:
- 函数作为返回值,上面的例子就是
- 函数作为参数传递,看以下例子
function F1() {
var a = 100 //还是来这里寻找
return function () {
console.log(a)
}
}
function F2(f1) {
var a = 200
console.log(f1())
}
var f1 = F1()
F2(f1) //100
解题
说一下对变量提升的理解
函数执行时会先创建当前的上下文环境,其中这两点会产生“变量提升”的效果
- 变量定义
- 函数声明
说明 this 几种不同的使用场景
- 作为函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind
创建 10 个<a>
标签,点击的时候弹出来对应的序号
错误的写法
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) //自由变量要去父级作用域取值 父作用域就是全局作用域
})
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)
})
document.body.appendChild(a)
})(i)
}
上面的回答已经结束了,但是还有一点可以优化,如果能做到,那将会给你加分。提示一下,是关于 DOM 操作的性能问题的。这里先按下不表,等后面讲解性能问题的时候再说。有兴趣的可以先去查查DocumentFragment
实际开发中闭包的应用
闭包的实际应用,主要是用来封装变量,收敛权限。即把变量隐藏起来,不让外面拿到和修改。
function isFirstLoad() {
var _list = []
return function (id) {
if (_list.indexOf(id) >= 0) {// 说明:有重复的 如果要检索的字符串值没有出现,则该方法返回 -1。
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
var i; //声明变量
for(i=0;i<10;i++){ //循环
(function(i){ //自执行的函数,就是不用调用,定义完成立即执行 的函数
//函数作用于
var a = document.createElement('button'); //创建标签
a.innerHTML = i+'<br />'; //html显示内容
a.addEventListener('click',function(e){ //添加事件
e.preventDefault(); //取消时间的默认动作
alert(i); //自由变量要去父作用域取值
})
document.body.appendChild(a) //添加到body里面
})(i)
}
知识点四:异步
什么是异步
先看下面的 demo,根据程序阅读起来表达的意思,应该是先打印100
,1秒钟之后打印200
,最后打印300
。但是实际运营根本不是那么回事。
console.log(100)
setTimeout(function () {
console.log(200)
}, 1000)
console.log(300)
再对比以下程序。先打印100
,再弹出200
(等待用户确认),最后打印300
。这个运行效果就符合预期要求。
console.log(100)
alert(200) // 1秒钟之后点击确认
console.log(300)
这俩到底有何区别?———— 第一个示例中间的步骤根本没有阻塞接下来程序的运行,而第二个示例却阻塞了后面程序的运行。前面这种表现就叫做异步(后面这个叫做同步)
为何需要异步呢?如果第一个示例中间步骤是一个 ajax 请求,现在网络比较慢,请求需要5秒钟。如果是同步,这5秒钟页面就卡死在这里啥也干不了了。
最后,前端 JS 脚本用到异步的场景主要有两个:
- 定时
setTimeout
setInverval
- 网络请求,如
ajax
<img>
加载 - 事件绑定(后面会有解释)click等
ajax 代码示例
console.log('start')
$.get('./data1.json', function (data1) {
console.log(data1)
})
console.log('end')
img 代码示例(常用语打点统计)
console.log('start')
var img = document.createElement('img')
img.onload = function () {
console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
事件绑定
console.log('start')
document.getElementById('btn1').addEventListener('click', function () {
alert('clicked')
})
console.log('end')
异步和单线程
JS 在客户端运行的时候,只有一个线程可运行,因此想要两件事儿同时干是不可能的。如果没有异步,我们只能同步干,就像第二个示例一样,等待过程中卡住了,但是有了异步就没有问题了。那么单线程是如何实现异步的呢?
console.log(100)
setTimeout(function () {
console.log(200)
})
console.log(300)
那上面的示例来说,有以下几点。重点从这个过程中体会单线程这个概念,即事情都是一步一步做的,不能两件事儿一起做。
- 执行第一行,打印
100
- 执行
setTimeout
后,传入setTimeout
的函数会被暂存起来,不会立即执行。 - 执行最后一行,打印
300
- 待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行。
- 发现暂存起来的
setTimeout
中的函数无需等待时间,就立即来过来执行
下面再来一个setTimeout
的例子。规则和上面的一样,只不过这里暂存起来的函数,需要等待 1s 之后才能被执行。
console.log(100)
setTimeout(function () {
console.log(200)
}, 1000)
console.log(300)
下面再来一个 ajax 的例子。规则也是一样的,只不过这里暂存起来的函数,要等待网络请求返回之后才能被执行,具体时间不一定。
console.log(100)
$.get('./data.json', function (data) {
console.log(200)
})
console.log(300)
最后再解释一下事件绑定,如下代码。其实事件绑定的实现原理和上面的是一样的,也是会把时间暂存,但是要等待用户点击只有,才能被执行。原理是一样的,因此事件绑定在原理上来说,可以算作是异步。但是从设计上来说,还是分开好理解一些。
console.log(100)
$btn.click(function () {
console.log(200)
})
console.log(300)
重点:异步的实现机制,以及对单线程的理解
下面的暂时先不讲
异步的问题和解决方案
异步遇到的最大的问题
- callback-hell
- 易读性差,即书写顺序和执行顺序不一致
console.log('start')
$.get('./data1.json', function (data1) {
console.log(data1)
$.get('./data2.json', function (data2) {
console.log(data2)
$.get('./data3.json', function (data3) {
console.log(data3)
$.get('./data4.json', function (data4) {
console.log(data4)
// ...继续嵌套...
})
})
})
})
console.log('end')
解答
同步和异步的区别是什么?分别举一个同步和异步的例子
同步会阻塞代码执行,而异步不会。alert
是同步,setTimeout
是异步
一个关于setTimeout
的笔试题
面试题中,setTimeout
的基本是必会出现的
// 以下代码执行后,打印出来的结果是什么
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
,不知道跟你答对了没有。具体的原理,我们后面再详细讲解。
前端使用异步的场景有哪些
- setTimeout setInterval
- 网络请求
- 事件绑定(可以说一下自己的理解)
知识点五:其他基础知识
正则表达式
test
函数的用法
var ua = navigator.userAgent
var reg = /\bMicroMessenger\b/i
console.log(reg.test(ua))
用于replace
的示例
function trim(str) {
return str.replace(/(^\s+)|(\s+$)/g, '')
}
match
函数的用法
var url = 'http://www.abc.com/path/xxx.html?a=10&b=20&c=30#topic' // 后面的 #topic 也可能没有
var reg = /\?(.+?)(#|$)/
var matchResult = url.match(reg)
console.log(matchResult[1]) // a=10&b=20&c=30
略过正则表达式,不讲
日期函数
日期函数最常用的 API 如下
Date.now() // 获取当前时间毫秒数
var dt = new Date()
dt.getTime() // 获取毫秒数
dt.getFullYear() // 年
dt.getMonth() // 月(0 - 11)
dt.getDate() // 日(0 - 31)
dt.getHours() // 小时(0 - 23)
dt.getMinutes() // 分钟(0 - 59)
dt.getSeconds() // 秒(0 - 59)
Math
Math 最常用的只有一个 API —— Math.random()
数组常用 API
- forEach
- every
- some
- sort
- map
- filter
forEach 举例
var arr = [1,2,3]
arr.forEach(function (item, index) {
// 遍历数组的所有元素
console.log(index, item)
})
every 举例
var arr = [1,2,3]
var result = arr.every(function (item, index) {
// 用来判断所有的数组元素,都满足一个条件
if (item < 4) {
return ture
}
})
console.log(result)
some 举例
var arr = [1,2,3]
var result = arr.some(function (item, index) {
// 用来判断所有的数组元素,只要有一个满足条件即可
if (item < 2) {
return ture
}
})
console.log(result)
sort 举例
var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a, b) {
// 从小到大排序
return a - b
// 从大到小排序
// return b - a
})
console.log(arr2)
map 举例
var arr = [1,2,3,4]
var arr2 = arr.map(function(item, index) {
// 将元素重新组装,并返回
return '<b>' + item + '</b>'
})
console.log(arr2)
filter 举例
var arr = [1,2,3]
var arr2 = arr.filter(function (item, index) {
// 通过某一个条件过滤数组
if (item >= 2) {
return true
}
})
console.log(arr2)
对象常用 API
- for-in
在看开源项目的过程中,经常会看到类似如下的源码。for...in循环对象的所有枚举属性,然后再使用hasOwnProperty()方法来忽略继承属性。
var obj = {
x: 100,
y: 200,
z: 300
}
var key
for (key in obj) {
// 注意这里的 hasOwnProperty,再讲原型链时候讲过了
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key])
}
}
解答
获取2017-06-10
格式的日期
function formatDate(dt) {
if (!dt) {
dt = new Date()
}
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
if (month < 10) {
// 强制类型转换
month = '0' + month
}
if (date < 10) {
// 强制类型转换
date = '0' + date
}
// 强制类型转换
return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
获取随机数,要求是长度一直的字符串格式
使用Math.random()
可获取字符串,但是返回的是一个小于 1 的小数,而且小数点后面长度不同
var random = Math.random()
var random = random + '0000000000' // 后面加上 10 个零
var random = random.slice(0, 10)
console.log(random)
写一个能遍历对象和数组的forEach
函数
遍历数组使用forEach
,而遍历对象使用for in
,但是在实际开发中,可以使用一个函数就遍历两者,jquery 就有这样的函数
function forEach(obj, fn) {
var key
if (obj instanceof Array) {
// 准确判断是不是数组
obj.forEach(function (item, index) {
fn(index, item)
})
} else {
// 不是数组就是对象
for (key in obj) {
fn(key, obj[key])
}
}
}
var arr = [1,2,3]
// 注意,这里参数的顺序换了,为了和对象的遍历格式一致
forEach(arr, function (index, item) {
console.log(index, item)
})
var obj = {x: 100, y: 200}
forEach(obj, function (key, value) {
console.log(key, value)
})
//遍历
var arr = [1,23,31,13];
arr.forEach(function(item,index){
console.log(index,item)
})
//所有的值都满足
var arr = [1,23,31,13];
var result = arr.every(function(item,index){
if(item<5){
return true
}
})
console.log(result)
//有一个值满足
var arr = [1,23,31,13];
var result = arr.some(function(item,index){
if(item<5){
return true
}
})
console.log(result)
//排序
var arr = [1,23,31,13];
var result = arr.sort(function(a,b){
return a-b
})
console.log(result)
//嵌入标签
var arr = [1,23,31,13];
var result = arr.map(function(item,index){
return '<b>'+item+'</b>'
})
console.log(result)
//过滤
var arr = [1,23,31,13];
var result = arr.filter(function(item,index){
if(item>2){
return true
}
})
console.log(result)
Math 是数学函数,但又属于对象数据类型 typeof Math => ‘object’
console.dir(Math) 查看Math的所有函数方法。
1,Math.abs() 获取绝对值
Math.abs(-12) = 12
2,Math.ceil() and Math.floor() 向上取整和向下取整
console.log(Math.ceil(12.03));//13
console.log(Math.ceil(12.92));//13
console.log(Math.floor(12.3));//12
console.log(Math.floor(12.9));//12
3,Math.round() 四舍五入
注意:正数时,包含5是向上取整,负数时包含5是向下取整。
1、Math.round(-16.3) = -16
2、Math.round(-16.5) = -16
3、Math.round(-16.51) = -17
4,Math.random() 取[0,1)的随机小数
案例1:获取[0,10]的随机整数
console.log(parseInt(Math.random()*10));//未包含10
console.log(parseInt(Math.random()*10+1));//包含10
案例2:获取[n,m]之间的随机整数
Math.round(Math.random()*(m-n)+n)
5,Math.max() and Max.min() 获取一组数据中的最大值和最小值
console.log(Math.max(10,1,9,100,200,45,78));
console.log(Math.min(10,1,9,100,200,45,78));
6,Math.PI 获取圆周率π 的值
console.log(Math.PI);
7,Math.pow() and Math.sqrt()
Math.pow()获取一个值的多少次幂
Math.sqrt()对数值开方
1.Math.pow(10,2) = 100;
2.Math.sqrt(100) = 10;