javascript基础的查缺补漏

对象转基本类型

let a = {
  valueOf() {
    return 0;
  },
  toString() {
    return '1';
  },
  [Symbol.toPrimitive]() {
    return 2;
  }
}
1 + a // => 3
'1' + a // => '12'

优先级: Symbol.toPrimitive>valueOf>toStringjavascript

'a'++'b'
由于+'b' 会被强制转换成NaN

function Foo() {
    return this;
}
Foo.getName = function () {
    console.log('1');
};
Foo.prototype.getName = function () {
    console.log('2');
};

new Foo.getName();   // -> 1
new Foo().getName(); // -> 2
优先级的顺序
new (Foo.getName());
(new Foo()).getName();
//new Foo() 是返回return的内容而不是去看构造函数的属性

如何成为全核工程师

先精后广,一专多长css

[]==false, ![]==true

js中隐式强制类型转换
转化为数字类型  false等于0, true等于1
若是是对象,先调用valueOf,若是没有用调用toString,这个过程叫ToPrimitive()
NaN==NaN  false
首先分析[]==![]
Boolean() 判断 0、-0、null、false、NaN、undefined、或者空字符串(""),则生成的 Boolean 对象的值为 false,
![] 变成!Boolean([]) 也就是!true,也就是false

[] + {} 和 {} + []同样吗

当有一个操做数是字符串时html

  • 若是两个操做数都是字符串,将两个字符串拼接起来
  • 若是只有一个操做符石字符串,则另外一个操做数转换为字符串(toString)

当有一个操做数是复杂数据类型(对象,数组)时,将两个操做数都转换为字符串(toString)相加vue

有一个操做数是简单数据类型(true/false, null,undefined) 时,同时不存在复杂数据类型和字符串,则将两个操做数都转换成数值(ToNumber)相加。java

[]+{}node

知足a有对象/数组,全部都转换为字符串拼接
""+"[object object]"="[object Object]"

1+{}react

知足第三条 a是对象/数组
"1[object Object]"

{}+[]git

"[object Object]"+toNumber([])
"[object Object]"+0

{}+{}github

"[object Object][object Object]"

函数的name属性

经过构造函数方式建立的函数name都是 'anonymous'

使用bind方法获得一个新的函数

function ff(){};
var f=ff.bind();
f.name 是bound ff

闭包

做用域是定义时的做用域,而不是执行时的做用域面试

闭包的使用场景

函数做为返回值

function F1(){
  var a = 100
  //返回一个函数(函数做为返回值)
  return function(){
      console.log(a)
  }
}

函数做为参数传递

function F1(){
  var a = 100
  return function(){
      console.log(a)   //自由变量,父做用域寻找
  }
}

一个函数执行若是造成一个不销毁做用域,保护里面的私有变量或者存储私有变量,可是闭包容易引发内存泄露

形成内存泄露的状况:

  • 全局变量
  • 不销毁做用域
function fn(){
    var a=1;
    return function(){
        a++
    }
}

做用域

全局:关闭浏览器的时候销毁
私有: 看是否返回地址而且被占用了,决定是否销毁
var 是没有块级做用域的

做用域链

是一个查找过程: 在一个私有做用域下遇到变量了,先看是否是本身私有的,若是不是往上一级找,没有继续找一只找到window下为之,没有就报错

上一级做用域

看当前整个做用域对应个人地址是在哪个做用域下定义的,那个做用域就是当前这个做用域的上一级

块级做用域

{}   if(){}    for(){}   while(){}

let 和const 定义的变量属于一个私有做用域,变量是私有变量

实例便可以经过构造函数中this.属性的方式获得私有属性还能够经过__proto__拿到所属类的原型的公有属性

词法做用域

常见的变量

javaScript 引擎

javaScript引擎是谷歌的v8引擎,这个引擎由两个部分组成

  • 内存堆:这是内存分配发生的地方
  • 调用栈:这是你的代码执行的地方

建立执行上下文有两个阶段

  • 建立阶段
  • 执行阶段

在建立阶段会发生三件事

  • this值的决定,就是this绑定
  • 建立词法环境组件
  • 建立变量组件

this的绑定

在全局执行上下文中,this的值指向全局对象,(在浏览器中,this引用window对象)

在函数执行上下文,this的值取决于该函数式如何被调用的,若是它被一个引用对象调用,那么this会被设置成那个对象,不然this的值被设置全局对象或者undefined(在严格模式下)

词法环境内部有两个组件

  • 环境记录器
  • 一个外部环境的引用

环境记录器是存储变量和函数声明的实际位置

外部环境的引用意味着他能够访问其父级词法环境(做用域)

词法环境有两种类型

全局环境(在全局执行上下文中)是没有外部环境引用的词法环境,

函数环境中,函数内部用户定义的变量存储在环境记录器中,而且引用的外部环境多是全局环境,或者任何包含此内部函数的外部函数。

简而言之

  • 在全局环境中,环境记录器是对象环境记录器
  • 在函数环境中,环境记录器是声明式环境记录器

值类型

// 值类型:Number、string、bollean、undefined
var a = 100
var b = a
a = 200
console.log(b) // 100 保存与复制的是值自己
typeof abc      //"undefined"
typeof null    //"object"
为何要说呢?由于我错了好屡次
typeof区分不了引用类型(除了函数)
用instanceof 来区分引用类型
alert(person instanceof Object); // 变量 person 是 Object 吗?

alert(colors instanceof Array); // 变量 colors 是 Array 吗?

alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?

引用传值

值传递和引用传递

function addNum(num)
{ 
 num+=10; 
 return num; 
} 
var num=10; 
var result=addNum(num); 
console.log(num);//10
console.log(result);//20
当为函数传递参数的时候,是将此值复制一份传递给函数,因此在函数执行以后,num自己的值并无改变,函数中的被改变的值仅仅是副本而已

function mutate(obj) {
  obj.a = true;
}

const obj = {a: false};
mutate(obj)
console.log(obj.a); // 输出 true

在值传递的场景中,函数的形参只是实参的一个副本(至关于a拷贝了一份),当函数调用完成后,并不改变实参
在引用传递的场景中,函数的形参和实参指向同一个对象,当参数内部改变形参的时候,函数外面的实参也被改变

function setName(obj){
    obj.name = '嘉明';
    obj = new Object();
    obj.name = '庞嘉明';
}
var person = new Object();
setName(person);
console.log(person.name); // '嘉明',为啥不是'庞嘉明'呢?
新建的对象 obj = new Object()
它本身空间保存的地址将会被新的对象的存储地址所覆盖,由于是传值不是引用,因此它不会影响到student空间所保存的地址,故最后虽然对obj的name属性从新赋值,但也丝绝不影响以前赋值结果,按值传递
你传进的一个对象 对象在内存地址中,即便你再外部赋值,可是内部 改变了,你外部就等于没有赋值

当取值为百分比时,须要特别注意:百分比不是相对于父元素的高度的,而是相对于父元素的宽度的heighttop的百分比取值,老是相对于父元素的高度

padding-topmargin-toppadding-bottommargin-bottom取值为百分比时,是相对于父元素的宽度

fixed问题

一提到position:fixed,天然而然就会想到:相对于浏览器窗口进行定位。

但其实这是不许确的。若是说父元素设置了transform,那么设置了position:fixed的元素将相对于父元素定位,不然,相对于浏览器窗口进行定位。

JavaScript 的怪癖

隐式转换

隐式转换为Boolean

if语句

字符串的隐式转换

加运算符(+) ,可是只要其中一个操做数是字符串,那么它执行链接字符串的操做

为何 ++[[]][+[]]+[+[]] = 10?

拆分
++[[]][+[]]
+
[+[]]
继续拆分

++[[]][0]
+
[0]

继续拆分

+([] + 1)
+
[0]

+([] + 1) === +("” + 1),而且 
+("” + 1) === +("1"),而且 
+("1") === 1 

最后简化为
1+[0]
[]==''  [0]=='0', [1]=='1'
因此 1+'0' ==='10'

2==true为何是false

由于==是比较值类型是否相等,true转换为数字是1 ,因此2==1为false

'2'==true '2' 隐式转化为2 2==true 为false

null >=0 ? true:false

null == 0  // false  null在设计上,在此处不尝试转型. 因此 结果为false. 
null > 0  // false   null 尝试转型为number , 则为0 . 因此结果为 false, 
null >= 0  // true 
null<=0     //true
那么你看
-null == 0  // true
+null == 0 // true
Number(null) == 0  // true

你不知道的JavaScript续集

数组

使用delete运算符能够将单元从数组中删除,可是请注意,单元删除后,数组的length属性并不会发生变化。
数组经过数字进行索引,但有趣的是它们也是对象,因此也能够包含字符串键值和属性(但这些并不计算在数组长度内)

var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2
若是字符串键值可以被强制类型转换为十进制数字的话,它就会被当作数字索引来处理
var a = [ ];
a["13"] = 42;
a.length; // 14

类数组
var a = { '0': 1, '1': 2, '2': 3, length: 3 };
function foo() {
    var arr = Array.prototype.slice.call(arguments);
    arr.push("bam");
    console.log(arr);
}
foo("bar", "baz"); // ["bar","baz","bam"]
同时用Array.from() 也能实现一样的功能
对伪数组或可迭代对象(包括arguments Array,Map,Set,String…)转换成数组对象

字符串

JavaScript中字符串是不可变的,而数组是可变的。

特殊的数字

var a=2/'foo';
a==NaN //false 
由于NaN===NaN  //false
可使用全局isNaN() 来判断一个值是不是NaN
可是 isNaN('abc')  //true
ES6开始咱们使用Number.isNaN()
var a = 2 / "foo";
var b = "foo";
Number.isNaN( a ); // true
Number.isNaN( b ); // false——好!

无穷数
var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity

零值    常规的0(也叫+0)和-0
var a = 0 / -3; // -0
var b = 0 * -3; // -0
从字符串转换为数字
+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0
咱们为何须要负零呢
数字的符号位用来表明其余信息(好比移动的方向)
此时若是一个值为0的变量失去了它的符号位,它的方向信息就会丢失。

值和引用

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// 而后
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
因为引用指向的是值自己而非变量,因此一个引用没法更改另外一个引用的指向

function foo(x) {
    x.push(4);
    x; // [1,2,3,4]
    // 而后
    x = [4, 5, 6];
    x.push(7);
    x; // [4,5,6,7]
}
var a = [1, 2, 3];
foo(a);
a; // 是[1,2,3,4],不是[4,5,6,7]
咱们没法自行决定使用值复制仍是引用复制,一切由值得类型来决定。
若是经过值复制的方式来传递复合值(如数组),就须要为其建立一个复本,这样传递的就再也不是原始值
foo(a.slice())

若是要将标量基本类型值传递到函数内并进行更改,就须要将该值封装到一个复合值(对象、数组等)中,而后经过引用复制的方式传递。
function foo(wrapper) {
    wrapper.a = 42;
}
var obj = {
    a: 2
};
// var obj=new Object();  obj.a=2;
foo(obj);
obj.a; // 42
与预期不一样的是,虽然传递的是指向数字对象的引用复本,但咱们并不能经过它来更改其中的基本类型值

function foo(x) {
    x = x + 1;
    x; // 3
}
var a = 2;
var b = new Number(a); // Object(a)也同样
foo(b);
console.log(b); // 是2,不是3
只是多数状况下咱们应该优先考虑使用标量基本类型

封装对象包装

var a=Boolean(false);
var b=new Boolean(false);
if (!b) {
console.log( "Oops" ); // 执行不到这里
}
咱们为false建立了一个封装对象,然而该对象是真值,因此这里使用封装对象获得的结果和使用false截然相反
若是想要自行封装基本类型值,可使用Object(...)函数(不带new关键字)
var a = "abc";
var b = new String( a );
var c = Object( a );
typeof a; // "string"
typeof b; // "object"
typeof c; // "object"
b instanceof String; // true
c instanceof String; // true

拆封

可使用ValueOf()函数
var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true

使用隐式拆封
var a = new String( "abc" );
var b = a + ""; // b的值为"abc"
typeof a; // "object"
typeof b; // "string"

原生函数做为构造函数

关于数组(array)、对象(object)、函数(function)和正则表达式,咱们一般喜欢以常量的形式来建立它们。实际上,使用常量和使用构造函数的效果是同样的(建立的值都是经过封装对象来包装)

var a = new Array( 1, 2, 3 );
a; // [1, 2, 3]
var b = [1, 2, 3];
b; // [1, 2, 3]
构造函数Array(..)不要求必须带new关键字。不带时,它会被自动不上。所以Array(1,2,3)和new Array(1,2,3)的效果是同样的。
Array构造函数只带一个数字参数的时候,该参数会被做为数组的预设长度(length),而非只充当数组中的一个元素

表达式的反作用

var a = 42;
var b = a++;
a; // 43
b; // 42 这是a++的反作用

========================
function foo() {
    a = a + 1;
}
var a = 1;
foo(); // 结果值:undefined。反作用:a的值被改变

你懂 JavaScript 嗎?

toJSON

var obj = {
    key: 'foo',
    toJSON: function () {
        return 'bar';
    }
};
var ret = JSON.stringify(obj);
console.log(ret);
区别很是明显,toJSON 的返回值直接代替了当前对象

Number

undefined   ---> NaN
null        ---> 0
boolean 的true为1  false便是0
string  -->数字或NaN
object      若定义valueOf优先用,其次toString

Number(undefined) // NaN
Number(null) // 0
Number(true) // 1
Number(false) // 0
Number('12345') // 12345
Number('Hello World') // NaN
Number({ name: 'Jack' }}) // NaN

const a = {
  name: 'Apple',
  valueOf: function() {
    return '999'
  }
}

Number(a) // 999

const a = new String('');
const b = new Number(0);
const c = new Boolean(false);

!!a // true
!!b // true
!!c // true

parseInt

参数:第一个参数是string 第二个参数是介于2和36之间的整数,一般默认为10,也就是咱们一般使用的十进制转换,若是是5就是5进制,超出这个范围,则返回NaN。若是第二个参数是0、undefined和null,则直接忽略
* 将字符串转为整数
* 若是字符串头部有空格,空格会被自动去除
* 若是参数不是字符串,先转为字符串再转换
parseInt('12px')  若是遇到不能转为数字的字符,就再也不进行下去,返回转好的部分
若是字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。
若是开头是0x按照16进制转换,若是是0按照10进制转换

又犯错了一次

const a = true;
const b = 123;

a === b // false
a == b // false
true强制转换为1

const a = '1,2,3';
const b = [1,2,3];
a === b // false
a == b // true
在a == b当中,阵列a因为没有valueOf(),只好使用toString()取得其基型值而获得字串'1,2,3',此时就可比较'1,2,3' == '1,2,3',所以是相等的(true)。

Object(null) 和Object(undefined) 等同于Object()也就是{}
var a = null;
var b = Object(a); // 等同於 Object()
a == b; // false

var c = undefined;
var d = Object(c); // 等同於 Object()
c == d; // false

var e = NaN;
var f = Object(e); // 等同於 new Number(e)
e == f;//false

避免修改原型的valueOf
Number.prototype.valueOf = function() {
  return 3;
};

new Number(2) == 3; // true

抽象的关系运算符

  • a <= b实际上是!(b > a),所以!false获得true。
  • a >= b实际上是b <= a也就是!(a > b)等同于!false获得true
const a = { b: 12 };
const b = { b: 13 };

a < b // false,'[object Object]' < '[object Object]'
a > b // false,其實是比較 b < a,即 '[object Object]' < '[object Object]'
a == b // false,其實是比較兩物件的 reference

a >= b // true
a <= b // true
`[]==[]`  false 由于两个的地址不是同样的

`'ab' < 'cd' // true `  以字典的字母顺序形式进行比较

'Hello World' > 1 // false,字串 'Hello World' 没法转化为数字,变成了NaN
NaN 不大于、不小于、不等于任何值,固然也不等于本身

图解构造器Function和Object的关系

//①构造器Function的构造器是它自身
Function.constructor=== Function;//true

//②构造器Object的构造器是Function(由此可知全部构造器的constructor都指向Function)
Object.constructor === Function;//true

//③构造器Function的__proto__是一个特殊的匿名函数function() {}
console.log(Function.__proto__);//function() {}

//④这个特殊的匿名函数的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true

//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函数
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true

Function instanceof Object 和 Object instanceof Function 运算的结果固然都是true啦

全部的构造器的constructor 都指向Function

Function 和prototype指向一个特殊匿名函数,而这个特殊匿名函数的 __proto__ 指向 Object.prototype

### Array.of

Array.of方法用于将一组值,转换为数组。
Array.from()
Array.from方法用于将两类对象转为真正的数组:相似数组的对象和可遍历(iterator)的对象(包括Map和Set)

建立包含N个空对象的数组

Array(3).fill().map(()=>({}))
Array.apply(null,{length:3}).map(()=>({}))

函数表达式

函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析。
console.log(sum(10 , 10)); //TypeError: sum is not a function
var sum = function(num1 , num2){
    return num1 + num2;
}

Javascript中Y组合子

递归就是函数不断调用自身
阶乘
let factorial=n=>n?factorial(n-1)*n:1;

const factorial = n => n === 1 ? 1 : n * factorial(n - 1)
优化
尾递归: 调用自身函数,计算仅用常量栈空间
const factorial = (n, total) => n === 1 ? total : factorial(n - 1, n * total)
优化
柯里化,将尾递归变为只接受单个参数的变量
const fact = (n, total = 1) => n === 1 ? total : fact(n - 1, n * total)

Lambda函数(匿名函数)
// ES5
var f = function (x) {
  return x;
};

// ES6
const f = x => x
lambda表达式写出递归(匿名函数递归)
将lambda表达式做为参数之一传入其身
const factorial= (f,n) => n===1 ? 1 : n*f(f,n-1);
factorial(factorial,6)
//这个也太难看了,解决方案柯里化
// 这块不怎么好懂我就忽略了

Lambda演算
Lambda演算中全部函数式匿名的,它们没有名称,只接受一个输出变量,即独参函数
构建一个高阶函数,它接受一个函数做为参数,并让这个函数将自身做为参数调用其自身:
const invokeWithSelf = f => f(f)
写个递归
const fact = (n, total = 1) => n === 1 ? total : fact(n - 1, n * total)
拿到前面的进行优化
const fact = f => (total = 1) => n => n === 1 ? total : f(f)(n * total)(n - 1)
const factorial = fact(fact)()

factorial(6) // => 720
构建Y
const fact = f => (total = 1) => n => n === 1 ? total : f(n * total)(n - 1)

const Y = f => (x => f(v => x(x)(v)))
               (x => f(v => x(x)(v))) // 瞧,这不就是黑魔法Y组合子嘛

const factorial = Y(fact)()

factorial(6) // => 720

尾调用优化

尾调用时指在函数return的时候调用一个新的函数,因为尾调用的实现须要存储到内存中,在一个循环体中,若是存在函数的尾调用,你的内存可能爆满或溢出。
尾调用实际用途——递归函数优化
在ES5时代,咱们不推荐使用递归,由于递归会影响性能。

可是有了尾调用优化以后,递归函数的性能有了提高。
const factorial = (n, total) => n === 1 ? total : factorial(n - 1, n * total)

let

let和const都可以声明块级做用域,let的特色是不会变量提高,而是被锁在当前块中。
 function test() {
        if(true) {
          console.log(a)//TDZ,俗称临时死区,用来描述变量不提高的现象
          let a = 1
        }
    }
    test()  // a is not defined

    function test() {
        if(true) {
          let a = 1
        }
        console.log(a)
    }    
    test() // a is not defined
    
 临时死区的意思是在当前做用域的块内,在声明变量前的区域叫作临时死区。

Object.is()

用来解决JavaScript中特殊类型 == 或者 === 异常的状况。
Object.is()来处理2个值的比较。
    console.log(Object.is(NaN, NaN)) // true
    console.log(Object.is(+0, -0)) // false
    console.log(Object.is(5, "5")) //false

解构赋值

function test(value) {
    console.log(value);
}
test({a=1,b=2}={a:2,b:3});

yield使用限制

yield只能够在生成器函数内部使用,若是在非生成器函数内部使用,则会报错。
   function *createIterator(items) {
        //你应该在这里使用yield
      items.map((value, key) => {
        yield value //语法错误,在map的回调函数里面使用了yield
      })
    }
    const a = createIterator([1, 2, 3]);
    console.log(a.next()); //无输出
    
在对象中添加生成器函数
    const obj = {
      a: 1,
      *createIterator() {
        yield this.a
      }
    }
    const a = obj.createIterator();
    console.log(a.next());  //{value: 1, done: false}

函数的caller

caller : 当前这个函数在哪一个函数调用的
function fn(){
    console.log(fn.caller);
}
function ff() {
    fn();
}
ff();//[Function: ff]
arguments.callee  就是当前函数自己
function fn(){
    console.log(argument.callee) 
}
fn.prototype.constructor===fn;//true  ,也表明的是函数自己

捕获和冒泡

xxx.onclick=function(){} //DOM0事件绑定,给元素的事件行为绑定方法,这些方法在事件传播的冒泡阶段(或者目标阶段)执行的
xxx.addEventListener('xxx',function(){},false)
//第三个参数false也是控制绑定的方法在事件传播的冒泡阶段执行,可是在捕获阶段执行没有实际意义,默认是false,能够不写

DOM0和DOM2的运行机制

DOM0事件绑定的原理:就是给元素的某一个事件私有属性赋值(浏览器会创建监听机制,当咱们出发元素的某个行为,浏览器会本身把属性中赋的值去执行)

DOM0事件绑定:只容许给当前元素的某个事件行为绑定一个方法,屡次绑定后面的内容会替换前面绑定的,以最后一次绑定的方法为主

DOM0事件绑定和DOM2事件绑定的区别

机制不同

  • DOM0采用的是私有属性赋值,全部只能绑定一个方法
  • DOM2采用的是事件池机制,因此能绑定屡次方法

移出的操做

let list = document.querySelector('#list');
    list.addEventListener('click',function (ev) {
        console.log(ev.target.innerHTML);
    })
    list.addEventListener('click',function () {
        console.log(2);
    })

    box.onclick=function(){}
    box.onclick=null// DOM0的移出(不须要考虑绑定的是谁)

//DOM2移出的时候
    function f3() {
        console.log(2);
    }
    list.addEventListener('click',f3);
    list.removeEventListener('click',f3);
        //DOM2移出的时候,必要清除移出的是哪一个方法技巧(不要绑定匿名函数,都绑定实名函数)

DOM0和DOM2是能够同时使用,由于是浏览器的两个运行机制,执行顺序和编写顺序有关

mouseenter和mouseover的区别

1. over属于滑过事件,从父元素进入子元素,属性离开父亲,会触发父元素的out,触发子元素的over
enter属于进入,从父元素进入子元素,并不算离开父元素,不会触发父元素的leave,触发子元素的enter
2. enter和leave阻止了事件的冒泡传播,而over和out还存在冒泡传播的

全部对于父元素嵌套子元素的这种状况,咱们用enter的使用会比over多一些

事件委托(事件代理)

给容器的click绑定一个方法,经过事件的冒泡传播机制,把容器的click行为触发,根据事件对象中的事件源(ev.target)来作不一样业务处理

<ul id="list">
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
    <li>item n</li>
</ul>
<script>
    let list = document.querySelector('#list');
    list.onclick=function (ev) {
        let target=ev.target||window.event.target;
        console.log(target.innerHTML);
    }
</script>

JQ的事件绑定

on/off : 基于DOM2事件绑定实现事件的绑定和移除

one:只绑定一次,第一次执行完成后,会把绑定的方法移出(基于on/off完成)

click/ mouseenter/... jq提供的快捷绑定方法,可是这些方法都是基于on/off完成的

delegate 事件委托方法(在1.7之前用的是live方法)

$(document).on('click',fn)
$(document).off('click',fn)
$(document).one('click',fn)
$(document).click(fn)

自执行匿名函数

任何消除函数声明和函数表达式间歧义的方法,均可以被解析器正确识别

针对这些一元运算符,到底用哪一个好呢,测试发现()的性能最优越

(function(){ /* code */ }());
!function(){alert('iifksp')}()        // true
+function(){alert('iifksp')}()        // NaN
-function(){alert('iifksp')}()        // NaN
~function(){alert('iifksp')}()        // -1

发布订阅设计模式(观察者模式)

思想:准备一个容器,把到达指定时候要处理的事情,事先一一增长到容器中(发布计划,而且向计划表中订阅方法),当到达指定时间点,通知容器中的方法依次执行

文章

forEach和map的区别

相同点

  • forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项)、index(索引值)、arr(原数组)

map

返回一个新数组,不会对空数组进行检测,不会该变原有数组

forEach

让数组每一项作一件事

空数组就不会执行回调函数

Promise A+规范

class Promise {
    constructor(excutorCallBack) {
        this.status = 'pending';
        this.value = undefined;
        this.fulfilledAry = [];
        this.rejectedAry = [];
        let resolveFn = result => {
            let timer=setTimeout(()=>{
                clearTimeout(timer);
                if (this.status !== 'pending') return;
                this.status = 'fulfilled';
                this.value = result;
                this.fulfilledAry.forEach(item=>item(this.value))
            })
        };
        let rejectFn = reason => {
            let timer=setTimeout(()=>{
                if (this.status !== 'pending') return;
                this.status = 'rejected';
                this.value = reason;
                this.rejectedAry.forEach(item => item(this.value));
            })
        };
        try{
            excutorCallBack(resolveFn, rejectFn());
        }catch(err){
            // 有异常信息按照rejected状态处理
            rejectFn(err);
        }
        excutorCallBack(resolveFn, rejectFn);
    }

    then(fulfilledCallBack, rejectedCallBack) {
        //处理不传递的情况
        typeof fulfilledCallBack!=='function'?fulfilledCallBack=result=>result:null;
        typeof rejectedCallBack!=='function'?rejectedCallBack=reason=>{
            throw new Error(reson.message);
        }:null;
        //返回一个新的promise实例
        return new Promise((resolve,reject)=>{
            this.fulfilledAry.push(()=>{
                try{
                    let x=fulfilledCallBack(this.value);
                    x instanceof Promise?x.then(resolve,reject):resolve(x);
                    // if(x instanceof Promise){
                    //     x.then(resolve, reject);
                    //     return;
                    // }
                    // resolve(x);
                }catch(err){
                    reject(err)
                }
            });
            this.rejectedAry.push(()=>{
                try{
                    let x=rejectedCallBack(this.value);
                    x instanceof Promise?x.then(resolve,reject):resolve(x);
                    // resolve(x);
                }catch(err){
                    reject(err)
                }
            });
        });
        // this.fulfilledAry.push(fulfilledCallBack);
        // this.rejectedAry.push(rejectedCallBack);
    }

    catch(rejectedCallBack) {
        return this.then(null,rejectedCallBack)
    }

    static all(promiseAry=[]){
        return new Promise((resolve, reject)=>{
            //index:记录成功的数量 result记录成功的结果
            let index=0,
                result=[];
            for (let i = 0; i <promiseAry.length; i++) {
                //promiseAry[i] 每个须要处理的promise实例
                promiseAry[i].then(val=>{
                    index++;
                    result[i]=val;
                    //索引须要和promiseAry对应,保证结果的顺序和数组的顺序一致
                    if (index === promiseAry.length) {
                        resolve(result);
                    }
                }, reject);
            }
        });
    }
}
module.exports = Promise;

call,apply,bind串联起来理解

cat.call(dog, a, b) = cat.apply(dog, [a, b]) = (cat.bind(dog, a, b))() = dog.cat(a, b)

本地存储和服务器存储

用到本地存储的地方:

  • 页面之间的信息通讯
  • 性能优化

session和cookie

session是服务器存储

  • 不兼容IE8及如下
  • 也有存储的大小限制,一个源下最多只能存储5MB内容
  • 本地永久存储,只要你不手动删除,永久存储在本地(可是咱们能够基于API removeItem/clear手动清除)
  • 杀毒软件或者浏览器的垃圾清除暂时不会清除localStorage(新版本谷歌会清除localStorage)
  • 在隐私或者无痕浏览下,是记录localStorage
  • localStorage和服务器没有半毛钱关系

cookie是客户端存储

  • 兼容全部的浏览器
  • 有存储的大小限制,通常一个源只能存储4kb内容
  • cookie有过时时间(当前咱们本身能够手动设置这个时间)
  • 杀毒软件或者浏览器的垃圾清理均可能会把cookie信息强制掉
  • 在隐私或者无痕浏览器模式下,是不记录cookie的
  • cookie不是严格的本地存储,由于要和服务器之间来回传输
localStorage.gsetItem([key],[value])//[value]必须是字符串格式(即便写的不是字符串,也会默认转换为字符串)
localStorage.getItem([key]) //经过属性名获取存储的信息
localStorage.removeItem([key])//删除指定的存储信息
localStorage.clear()//清除当前域下存储的全部信息
localStorage.key(0)//基于索引获取指定的key名

设置cookie

数组的方法

flex

文档

须要一个容器 display:flex
flex-direction (元素排列方向)
    row, row-reverse, column, column-reverse
flex-wrap (换行)
    nowrap, wrap, wrap-reverse
flex-flow (以上二者的简写)
    flex-direction || flex-wrap
justify-content (水平对齐方式)
    flex-start, flex-end, center, space-between, space-around
align-items (垂直对齐方式)
    stretch, flex-start, flex-end, center, baseline
align-content (多行垂直对齐方式)
    stretch, flex-start, flex-end, center, space-between, space-around

递归的本质是栈的读取

在算法中咱们会遇到不少递归实现的案例,全部的递归均可以转换成非递归实现,

其转换的本质是:递归是解析器(引擎)来帮咱们作了栈的存取,非递归是手动建立栈来模拟栈的存取过程

递归组件能够转换成扁平数组来实现:

更改DOM结构成平级结构,点击节点以及节点的视觉样式经过操做总的list数据区实现

而后使用虚拟长列表来控制vue组组建实例建立的数量

性能优化

减小DNS查找,避免重定向

DNS:负责将域名URL转化为服务器主机IP
DNS查找流程:首先查看浏览器缓存是否存在,不存在则访问本机DNS缓存,再不存在则访问本地DNS服务器。因此DNS也是开销,一般浏览器查找一个给定URL的IP地址要花费20-120ms,在DNS查找完成前,浏览器不能从host那里下载任何东西。 
当客户端的DNS缓存为空时,DNS查找的数量与WEB页面中惟一主机名的数量相等,因此减小惟一主机名的数量就能够减小DNS查找的数量

资源async defer

defer

若是script设置了该属性,则浏览器会异步的下载该文件,而且不会影响后续DOM的渲染

若是有多个设置了deferscript标签存在,则会按照顺序执行全部的scriptdefer脚本会在文档渲染完毕后,DOMContentLoaded事件调用前执行。

<script defer src='1.js'></script>
<script>
    window.addEventListener('DOMContentLoader',function(){
    console.log('DOMContentLoader')
})
 </script>

async

async的设置,会使得script脚本异步的加载并在容许的状况下执行 async的执行,

并不会按着script在页面中的顺序来执行,而是谁先加载完谁执行。

推荐使用场景

defer 若是你的脚本代码依赖于页面中的DOM元素(文档是否加载解析完毕),或者被其余脚本文件依赖

  • 评论框 代码语法高亮

页面静态直出

  • 就是浏览器直接输出渲染好数据的html页面(简称直出)
  • 直出就是须要node.js的支持,服务器上的浏览器渲染好的东西,直接输出给客户端的浏览器
  • 简单来讲,就是直接把配件选好,让店家帮忙组装器,一次性发过来,就是直出这个道理

重读《JavaScript高级程序设计》

arguments对象是类数组

apply()方法接受两个参数:一个是运行函数的做用域,另外一个是参数数组,这个参数数组能够是Array实例,也能够是arguments对象(类数组对象)

function sum(num1 , num2){
    return num1 + num2;
}
function callSum1(num1,num2){
    return sum.apply(this,arguments); // 传入arguments类数组对象
}
function callSum2(num1,num2){
    return sum.apply(this,[num1 , num2]); // 传入数组
}
console.log(callSum1(10 , 10)); // 20
console.log(callSum2(10 , 10)); // 20

Object.create()和new object()和{}的区别

Object.create()

  • Object.create(null) 建立的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法

  • Object.create()方法接受两个参数:Object.create(obj,propertiesObject) ;

    obj:一个对象,应该是新建立的对象的原型。

    propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新建立的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数同样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。

    var o = Object.create(Object.prototype, {
      // foo会成为所建立对象的数据属性
      foo: { 
        writable:true,
        configurable:true,
        value: "hello" 
      },

面试

跨标签页通信??

浏览器下事件循环

事件循环是指:执行一个宏任务,而后执行清空微任务列表,循环再执行宏任务,再清微任务列表

从输入 url 到展现的过程

  • DNS解析

  • TCP三次握手

  • 发送请求,分析url,设置请求报文(头,主体)

  • 服务器返回请求的文件(html)

  • 浏览器渲染

    • html parse==>DOM Tree

      标记化算法,进行元素状态的标记

      dom树构建

    • css parser==>Styel tree

      解析css代码,生成样式树

    • attachment==>Render Tree

      结合dom树与style树,生成渲染树

    • layout:布局

    • GPU painting:像素绘制页面

内存泄露

  • 意外地全局变量,没法被回收
  • 定时器:未被正确关闭,致使所引用的外部变量没法被释放
  • 事件监听:没有正确销毁
  • 闭包:会致使父级中的变量没法被释放
  • dom引用: dom元素被删除时,内存中的引用未被正确清空

剑指offer??

描述建立一个对象的过程

  • 新生成了一个对象
  • 连接到原型
  • 绑定this
  • 返回新对象

JQ的attr和prop的区别

从源码来看:

  • attr是经过setAttributegetAttribute来设置的,使用的是DOM属性节点
  • prop是经过document.getElementById(el)[name]=vlaue来设置的,是转化为js对象的属性
  • 经过设置checked,selected,readonly,disabled等的时候使用prop效果更好,减小了访问dom属性节点的频率。
  • 通常若是是标签自身自带的属性,咱们用prop方法来获取;若是是自定义的属性,咱们用attr方法来获取。

DOM节点的attr和property有何区别

  • property只是一个JS对象的属性的修改
  • Attribute是对html标签属性的修改

获取当前时间

new Date().toISOString().slice(0,10)

toLocaleString()

方法返回一个字符串表示数组中的元素,数组中的元素使用各自toLocaleString方法转成字符串,这些字符串将使用一个特定语言环境的字符串

var number = 1337;
var date = new Date();
var myArr = [number, date, "foo"];
var str = myArr.toLocaleString(); 
console.log(str); 
// 输出 "1,337,2019/2/15 下午8:32:24,foo"

let a=3500
a.toLocaleString() //3,500

写React/Vue项目时为何要在组件中写key,其做用是什么

key的做用是为了在diff算法执行时更快的找到对应的节点,提升diff速度

undefined.toString()

和null.toString()同样也是报错的,原始数据类型存储的是值,是没有函数能够调用的

构造函数的返回值

  • 没有返回值则按照其余语言同样返回实例化对象

  • 如有返回值则检查其返回值是否为引用类型。若是是非引用类型,如基本类型(string,number,boolean,null,undefined)则与无返回值相同,实际返回其实例化对象。

  • 若返回值是引用类型,则实际返回值为这个引用类型。

相关文章
相关标签/搜索