每天一点,JavaScript【踩坑记录】

前端之家收集整理的这篇文章主要介绍了每天一点,JavaScript【踩坑记录】前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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'

基本类型

UndefinedNullBooleanNumberStringSymbol( 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

深拷贝: 能够真正意义上的数组和对象的拷贝,(拷贝之后,任何一方修改,都不会反应给另一方)

  1. 通过递归调用浅拷贝的方式(属性类型为对象时,进行遍历属性内的属性,最终都是进行基本类型的拷贝)

  2. 借用JSON全局对象
    缺点是只能拷贝对象只有 Number,String,Boolean,Array,扁平对象,即那些能够被 json 直接表示的数据结构,对象中存在方法是不能被拷贝过来勒

    函数不能被很好地处理
  3. 利用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 = null
valueOf()引用类型返回本身

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

cover by

如果没有if语句

switchif-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,或者breakcontinue
设计模式使用策略模式

//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>

mixin - npm

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>

猜你在找的JavaScript相关文章