MDN
判断是不是函数
Object.prototype.toString.call(callback) != "[object Function]"
创建指定长度的空数组
new Array(10) //(10)[empty × 10]
闭包速览
全局变量
f2 = function(){
alert(a)
}
return f2;
}
var result = f1();
result(); //999
nAdd(); //相当于一个setter,可以在函数外部操作函数内部变量的值
result(); //1000,f2()被执行了2次
闭包的概念
函数内的函数
闭包的用途
可以读取函数内部的变量
让父函数的变量始终保持在内存中
注意:要想保持父函数的变量值不变,需要把父函数当作对象使用
函数作对象
var name = 'The Window';
var object = {
name : 'The Object',getName : function(){
return function(){
return this.name
}
}
}
alert(object.getName()()) //The Window
Object绑定作用域;
var that = this;
alert(object.getName()()) //The Object
Obejct[string]
数组的定义:一个存储元素的线性集合(collection),元素可以通过索引
来任意存取,索引通常时数字,用来计算元素之间存储位置的偏移量。
JavaScript中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的数组,即索引可以是整数也可以不是。然而,这些索引在内部被转换为字符串类型
,因为javascript对象中的属性名必须是字符串。数组在JavaScript中只是一个特殊的对象,所以效率不如其他语言中的数组高
// 这种模式的优势
obj['rock and roll'] = 'hape'
obj['姓名'] = 'Niko'
基本类型
Undefined
、 Null
、 Boolean
、 Number
、 String
、 Symbol( new in es6)
- 基本类型的值是不可用变的
- 基本类型的比较是它们的值的比较
- 基本类型的变量是存放在栈内存(Stack)里的
引用类型
统称为 Object 类型
。细分的话,有:Object 类型
、Array 类型
、Date 类型
、RegExp 类型
、Function 类型
等。
- 引用类型的值是可变的
- 引用类型的比较是引用的比较
- 引用类型的值是保存在堆内存(Heap)中的对象(Object)
与其他编程语言不同,JavaScript 不能直接操作对象的内存空间(堆内存)。
链接:
栈/堆
JavaScript的变量的存储方式 -- 栈(stack)和堆(heap)
栈: 自动分配内存空间,系统自动释放,离 main存放的是基本类型的值和引用类型的地址
堆: 动态分配的内存,大小不定,也不会自动释放。里面存放引用类型的值
自我理解:栈是增加一个变量就会分配一个内存空间,使用完之后自动回收;堆是增加的变量如果引用存在的引用类型的变量,就不会分配内存
值传递/址传递
基本类型和引用类型最大的区别实际就是传值和传址的区别
值传递:基本类型采用的值传递
址传递:引用类型则是地址传递,将存放在栈内存中的地址赋值给接收的对象
浅拷贝/深拷贝
浅拷贝: 先设置一个新的对象obj2,通过遍历的方式将obj1对象的值一一赋值给obj2对象,(其中一方修改值,都会反应到另一方)
const Sub = {
...Sup
}
Sup.info.age = 24
console.log(Sub.info.age) //24
深拷贝: 能够真正意义上的数组和对象的拷贝,(拷贝之后,任何一方修改,都不会反应给另一方)
-
通过递归调用浅拷贝的方式(属性类型为对象时,进行遍历属性内的属性,最终都是进行基本类型的拷贝)
-
借用JSON全局对象
缺点是只能拷贝对象只有 Number,String,Boolean,Array,扁平对象,即那些能够被 json 直接表示的数据结构,对象中存在方法是不能被拷贝过来勒函数不能被很好地处理
-
利用Object.defineProperty()赋值
缺点:仅IE9+滋瓷
数据类型转换
本文不对使用的API做更深入的介绍
仅当做字典查阅
任何转string
str = String(str)
str = str.toString()
string转array
arr = str.split() //(",") () length=1 ("") | length=str单位个数
array转string
str = arr.join("")
string转number
num = Number(str)
num = parseInt(str) //仅为整数时
num = parseFloat(str) //返回浮点数
number转string
str = num.toFixed() //仅整数
str = num.toExponential()//指数计数
str = num.toPrecision()//指针计数
number转array
arr = num.toString().split()
array转number
num = Number(arr.toString())
类型判断
typeof
instanceof
constructor
prototype
操作符
ECMA-262描述了一组用于操作数据值的操作符,包括算数操作符(如加号和减号)、位操作符、关系操作符和相等操作符,ECMAScript操作符的与众不同之处在于,它们能够适用于很多值,例如字符串、数字值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的valueOf()
和(或)toString()
方法,以便取得可以操作的值。
规则
- 应用于
包含有效数字字符的字符串
时,先将其转换为数字值,再执行相应操作符的操作。 - 应用于
不包含数字的字符串
时,将变量的值设置为NaN
,字符串变量变成数值变量。 - 应用于布尔值
false
时,先将其转换为0
,再执行相应操作符的操组。 - 应用于布尔值
true
时,先将其转换为1
,再执行相应操作符的操组。 - 应用于
对象
时,先调用对象的valueOf()
方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN
或者没有valueOf()
方法,则在调用toString()
方法后再应用前述规则。对象变量变成数值变量。
案例
var s1 = 1.1;
--s1 //0.10000000000000009
null == undefined // true
[] == ![] // true
/**
1.右边![] 先进行Boolean转换 false,再进行Number转换 0
2.变成 [] == 0 (即obj == number),左边对象调用valueOf()等于对象本身,继续toString() 得到 “”
3.变成 “” == 0 (即string == number)""空字符串,进行Number转换 Number("")=0
所以返回true
**/
["x","y"] == "x,y" // true
"23" < "3" // true
"23" < 3 // false
5 - "" // 5 ""被转成了0
5 - “2” // 3
5 - null // 5 null被转成了0</code></pre>
双感叹号!!
var a =''
!!a 等价于 Boolean(a)
其他
null
为空对象,创建对象赋值前,最好用var obj = nullvalueOf()
引用类型返回本身
num1+''
转为string~~double1
转为int(整数型)1*str1
转为float
this 黄金四法则
1.在全局函数
中,this等于window
2.当通过call()或apply()改变函数执行环境的情况下,this指向其他对象
3.当函数作为某个对象的方法
调用时,this等于那个对象
4.匿名函数
的执行环境具有全局性,this指向window
非严格模式下才有效
var a = 3;
(function(){
var a = 4;
console.log(a); //4
console.log(this.a) //3
})()
背下这四句话,this问题引刃而解。补充
5.箭头函数与this
全局函数
//全局函数 this作用域不变
var a = 'Niko';
func = ()=>{
console.log(this.a) //Niko
}
//等同于
function func(){
console.log(this.a) //Niko
}
闭包函数
var a ='Niko';
var obj = {
a : 'Bellic',func : function(){
return ()=>{console.log(this.a)}
}()
}
obj.func() //Bellic
//等同于
var obj = {
a : 'Bellic',func : function(){
var that = this; //要先获得obj内的a,必须改变this指向
return function(){
console.log(that.a)
}
}()
}
obj.func() //Bellic
call
apply
bind
函数柯里化
函数式编程
链式调用函数
{
return (_c)=>{
console.log(_b,_c)
}
}
_a("bb")("cc") //bb cc 柯里化
递归
斐波那契数列
自己调用自己,称为递归调用arguments.callee()
argument为函数内部对象,包含传入函数的所有参数,arguments.callee代表函数名,多用于递归调用,防止函数执行与函数名紧紧耦合的现象,对于没有函数名的匿名函数也非常起作用
function f(n){
if(n==1){
return 1
}else if(n==2){
return 2
}else {
return f(n-1)+f(n-2)
}
}
尾递归
return _fibonacci(n,1)
}</code></pre>
在ES6中尾递归可以这样写,不过需要在'use strict'
模式下
ES6 解构赋值
利用惰性单例缓存对象进行优化
惰性函数
惰性函数就是解决每次都要进行判断的这个问题,解决原理很简单,重写函数。
实例:兼容现代浏览器与IE的addEvent事件
一般写法
惰性函数写法
立即执行函数IIFE
(function(global){
console.log(global) //window
//构造函数
function Set(){
this._values = []
}
Set.prototype['log'] = function(){console.log(this._values)}
global.Set = Set;
})(this)
//使用
var set = new Set()
set.log() //[]
玩具语言到底要不要加分号
for循环
while 循环
相对于for循环,while常用于将循环这一过程包装起来,将循环次数交给外部来决定。
经典案例:二叉查找树的插入方法(利用引用类型值共享特性)
data){
if(current.left == null){
current.left = n
break;
}else {
current = current.left
}
}else {
if(current.right == null){
current.right = n
break;
}else {
current = current.right
}
}
}
原型链继承
Tea.prototype = new beverage()
set/get
obj.foo = 1 //set val
obj.foo //get val \n 1
如果没有if语句
switch
和if-else
相比,由于使用了Binary Tree算法,绝大部分情况下switch会快一点,除非是if-else的第一个条件就为true.
只是在实际开发中 没有人会去用很多很多else if的
都是用 switch case 的 后者比较清晰 给人感觉就是一个脑子很清楚的人写出来的东西
代码块1
break;
case 2:
//执行代码块2
break;
case 3:
//执行代码块3
break;
default:
//n 与 case 1 和 case 2 不同时执行的代码
}
在循环中判断使用do-while
,或者break
、continue
。
设计模式使用策略模式
//do-while
do{
i+=2
} while (i<10)
//while
while(i<10){
i+=2
}
//break和continue
[labelName :] for(var j=0;j<10;j++){
if(j%5 === 0){
break [labelName]; //中断并跳出循环
continue [labelName]; //仅仅中断这一步但会继续执行之后的循环
}
i++
}
</code></pre>
利用对象属性
Mixin 模式
Mixin
模式,就是对象继承的一种替代方案,中文译为“混入”(min in),意为在一个对象之中混入另外一个对象的方法
如维基百科中所定义的,mixin是一个包含其他类使用方法的类,而不必是其他类的父类。
换句话说,mixin提供了实现某种行为的方法,但我们不单独使用它,我们使用它来将行为添加到其他类。
object.assign(MyClass.prototype,Foo)
let obj = new MyClass()
obj.foo() //foo
//通用脚本
export function mixins(...list){
return function(target){
object.assign(target.prototype,...list)
}
}
//使用 Decorator修饰器
import {mixins} from './mixins'
const Foo = {
foo(){console.log('foo')}
}
@mixins(Foo)
class MyClass {}
let obj = new MyClass()
obj.foo() //foo</code></pre>
function Foo(){
}
Foo.prototype = {
t1: function(){ return 'skT1' }
}
Foo = mixin(Foo,EventEmitter)</code></pre>
this is the equivalent of:
Foo.prototype = Object.create(EventEmitter.prototype)</code></pre>
逗号操作符
1.使用逗号操作符可以在一条语句中执行多个操作
2.逗号操作符多用于声明多个变量
,还可以用于附值
3.用于附值时,逗号操作符总会返回表达式中的最后一项
var i,j,k;
for(i=0,j=0; i<10,j<6;i++,j++;){
k = i + j
}
console.log(k) //5+5=10
//i<6,j<10呢 9+9=18</code></pre>
form
数组
concat
push
的不改变源数组的替代方法:concat
。
concat(arr1,arr2) 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
Function == Object???
// foo.getName 这里foo居然可以被视为对象??
foo.getName = function(){
console.log(1)
}
foo.prototype.getName = function(){
console.log(2)
}
foo.getName() //1
new foo().getName() //2</code></pre>
变量提升
function getName(){
console.log(2)
}
getName() // 1</code></pre>
变量替换
b = 1
var b = 2
console.log(b) //2</code></pre>