Git Product home page Git Product logo

blog's Introduction

Hi there 👋

🔭 目前任职于快手,有需要内推的可以发邮件至 [email protected]

Languages and Tools:

Qiang's github stats

blog's People

Contributors

wuxianqiang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

blog's Issues

内存释放

  1. 当函数执行返回一个引用数据类型的值,并且在函数的外面被其他东西接收了,这种情况一般不销毁。
function fn(){
    return function (){  }
}
var f = fn();
  1. 在一个私有作于域中给DOM元素的事件绑定方法,一般情况不销毁。
~function (){
    div.onclick = function (){  }
}
  1. 函数执行一次然后把返回值值执行一次,当返回值执行完成后才把它销毁。
function fn(){
    return function (){  }
}
fn()()

for-in循环遍历

for-in 循环再遍历的时候,默认的话可以把自己私有的和它所属类的原型上拓展的属性和方法都可以遍历到,但是一般情况下只要遍历私有的属性,解决方法:

for (var key in object) {
    if (object.hasOwnProperty(key)) {
        
    }
}

jQuery源码阅读

在jQuery中我们通常通过$()传入一个选择器来获取一个元素,然后会返回一个jQuery对象,把匹配的元素放到一个类似数组的集合中,而且执行完了还可以执行下一个函数,jQuery如此强大,我们就来分析一下它背后的原理。

理解jQuery的原型

先看看$()执行都经历了哪些过程,下面是经过删减后的代码

var jQuery = function () {
    return new jQuery.fn.init()
};
jQuery.fn = jQuery.prototype = {
    constructor: jQuery,
    length: 0
}
var init = jQuery.fn.init = function () {
    // 初始化函数
}
init.prototype = jQuery.fn

通过上面的代码我们可以得到下面这样的原型图
图片
我们发现jQuery函数和init函数共享同一个原型,这就是jQuery支持链式写法的原因

提示:每次执行$()都会初始化一个实例,如果你每次获取一个元素都初始化一个实例,那么就会消耗更多的性能,建议用变量保存这个值方便以后使用。

理解扩展函数extend

这是 jQeury 核心中很重要的一个函数 . 通过它我们就可以轻松地在 jQuery 或者 jQuery 原型对象中随意扩展自己想要的方法

jQuery.extend = jQuery.fn.extend = function () {
   var options, name, src, copy, copyIsArray, clone,
       target = arguments[0] || {},
       i = 1,
       length = arguments.length,
       deep = false;
   // 如果传进来的首个参数是一个 boolean 类型的变量,那就证明要进行深度拷贝。而这时传进来的 argumens[1] 就是要拷贝的对象
   if (typeof target === "boolean") {
       deep = target;
       target = arguments[i] || {};
       i++;
   }
   //  如果 target 不是 objet  并且也不是 function  就默认设置它为 {}
   if (typeof target !== "object" && !jQuery.isFunction(target)) {
       target = {};
   }
   //如果只传入了一个参数 ,  那么扩展的就是 jQuery 自身 
   if (i === length) {
       target = this;
       i--;
   }
   for (; i < length; i++) {
       if ((options = arguments[i]) != null) {
           for (name in options) {
               src = target[name];
               copy = options[name];
               // 不把自己的引用作为自己的一个成员
               if (target === copy) {
                   continue;
               }
               // options 是扩展对象 ,  它的方法或属性将会被扩展到 target 上
               if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
                   if (copyIsArray) {
                       copyIsArray = false;
                       clone = src && Array.isArray(src) ? src : [];

                   } else {
                       clone = src && jQuery.isPlainObject(src) ? src : {};
                   }
                   // 使用递归的方法实现深度拷贝
                   target[name] = jQuery.extend(deep, clone, copy);
               } else if (copy !== undefined) {
                   target[name] = copy;
               }
           }
       }
   }
   return target;
};

通过阅读extend源码我们发现,不仅可以通过$.extend()在jQuery中添加静态方法和通过$.fn.extend()在jQuery原型中添加公共方法之外,而且还实现了像ES6中Object.assign()方法的对象合并功能,而且jQuery更强大,支持深浅拷贝,只需在第一个参数中传入true/false,ES6的那个方法是不支持深拷贝的

$.extend({},{name: "张三", age: 18})
Object.assign({},{name: "张三", age: 18})

理解初始化函数init

  init = jQuery.fn.init = function (selector, context, root) {
    var match, elem;
    // 没有传入参数会返回一个空对象,如:$()
    if (!selector) {
        return this;
    }
    root = root || rootjQuery;
    // 传入字符串的时候分别对有上下文和没有上下文做了处理
    if (typeof selector === "string") {
        if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) {
            match = [null, selector, null];
        } else {
            match = rquickExpr.exec(selector);
        }
        if (match && (match[1] || !context)) {
            if (match[1]) {
                context = context instanceof jQuery ? context[0] : context;
                // 调用 jQuery.merge 把两个数组拼接起来 ( 将第二个数组接到第一个数组的尾巴上 )
                // 调用 jQuery.parseHTML 将字符串转化成真正的 DOM 元素然后放在一个数组里面 
                jQuery.merge(this, jQuery.parseHTML(
                    match[1],
                    context && context.nodeType ? context.ownerDocument || context : document,
                    true
                ));
                if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
                    for (match in context) {
                        if (jQuery.isFunction(this[match])) {
                            this[match](context[match]);
                        } else {
                            this.attr(match, context[match]);
                        }
                    }
                }
                return this;
            } else {
                // 推荐使用ID选择器来匹配元素从而提高性能!
                elem = document.getElementById(match[2]);
                if (elem) {
                    this[0] = elem;
                    this.length = 1;
                }
                return this;
            }
            // 参数还可以传入一个表达式哦!如:$("#"+"box") 相当于 $("#box")
        } else if (!context || context.jquery) {
            return (context || root).find(selector);
        } else {
            return this.constructor(context).find(selector);
        }
    } else if (selector.nodeType) {
        // 参数可以传入一个DOM元素,如:$(document.getElementById('box')) 相当于 $("#box")
        this[0] = selector;
        this.length = 1;
        return this;
    // 参数可以传入一个函数,如:$(function (){}) 相当于 window.onload = function() {}
    } else if (jQuery.isFunction(selector)) {
        return root.ready !== undefined ?
            root.ready(selector) :
            selector(jQuery);
    }
    // 最后这条语句是将传入的一个参数转换为jQuery对象返回,如:$(this)
    return jQuery.makeArray(selector, this);
};
  • 直接的一个 Dom 元素类型
  • 数组类型
  • 函数类型
  • jQuery 或者其他的类数组对象类型
  • string 类型
    • 没有 context 的情况
    • 有 context 的情况

链式写法的原因

支持链式写法和下面连个函数有关,我们来看看

jQuery.fn = jQuery.prototype = {
    pushStack: function (elems) {
        var ret = jQuery.merge(this.constructor(), elems);
        ret.prevObject = this;
        return ret;
    },
    end: function () {
        return this.prevObject || this.constructor();
    }
}

pushStack使用传入的元素生成一个新的 jQuery 元素 , 并将这个对象的 prevObject 设置成当前这个对象 (this). 最后将这个新生成的 jQuery 对象返回,end方法是我返回之前设置的对象prevObject

有很多的函数 return this,这就是为什么支持链式写法的原因,但是每次都链式调用的同时都不能修改我之前匹配到的那个元素集合,如果某一个方法真的要修改匹配元素集合 , 那么它就会调用 pushStack 方法 , 把当前的 jQuery 对象保存起来 , 以便以后使用 end方法恢复这个 jQuery 对象 。

怎样拷贝数组(深/浅拷贝)

拷贝数组就是把原来数组的每一项保存在一个新数组中,这样在操作数组的同时,另一个数组就不会受影响,因为数组是引用类型的值,所以在拷贝数组的同时并不能直接复制变量值,我们有一下几种方法可以使用。

循环

var ary1 = [1,2,3];

for (var i = 0, ary2 = Array(ary1.length); i < ary1.length; i++) ary2[i] = ary1[i];

slice

var ary1 = [1,2,3];

var ary2 = ary1.slice();

运算符

var ary1 = [1,2,3];

var ary2 = [...ary1];

from

var ary1 = [1,2,3];

var ary2 = Array.from(ary1);

concat

var ary1 = [1,2,3];

var ary2 = [].concat(ary1);

说到深拷贝,比较特殊的就是数组和对象了,所以在编写方法的时候要分别做不同的处理,代码如下:

let ary = [1,[2, {name: "张三"}]];

function copyAry(options) {
    return copy(options)
}

function copy(ary) {
    let newAry = []
    for (const item of ary) {
        let value = item;
        if (Object.prototype.toString.call(value) === "[object Object]") value = copyObj(value);
        if (Object.prototype.toString.call(value) === "[object Array]") value = copyAry(value);
        newAry.push(value);
    }
    return newAry;
}

function copyObj(obj) {
    let newObj = {};
    for (const key in obj) {
        let value = obj[key];
        if (Object.prototype.toString.call(value) === "[object Object]") value = copyObj(value);
        if (Object.prototype.toString.call(value) === "[object Array]") value = copyAry(value);
        newObj[key] = value;
    }
    return newObj;
}

// 测试
let newAry = copyAry(ary);
ary[1][1].name="李四"
console.log(newAry[1][1].name) //张三
console.log(ary[1][1].name) //李四

jQuery源码阅读二

function DOMEval( code, doc ) {
		doc = doc || document;
		var script = doc.createElement( "script" );
		script.text = code;
		doc.head.appendChild( script ).parentNode.removeChild( script );
	}

DOMEval: 该方法通过动态创建一个script标签,然后把标签添加到页面中,把代码执行,然后移除script标签,该方法并没有使用eval,因为jQuery使用了严格模式use strict,在严格模式中不能使用eval,该方法就是等同于eval。参数说明:参数一是代码字符串,参数二是添加到页面的位置。

eq: function( i ) {
		var len = this.length,
			j = +i + ( i < 0 ? len : 0 );
		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
	}

jQuery的原型中有个eq的方法,看上面的代码,我们发现它对参数的处理非常好,首先是+i把传入进来的参数i转换数值形式的,然后是判断i是正数还是负数,如果传入的是负数默认会与数组的长度相加得到正确的值。该方法使用了一个pushStack方法,我们看看该方法是干什么的?

pushStack: function( elems ) {
		var ret = jQuery.merge( this.constructor(), elems );
		ret.prevObject = this;
		return ret;
	},
merge: function( first, second ) {
		var len = +second.length,
			j = 0,
			i = first.length;
		for ( ; j < len; j++ ) {
			first[ i++ ] = second[ j ];
		}
		first.length = i;
		return first;
	},
end: function() {
		return this.prevObject || this.constructor();
	}
  1. pushStack是jQuery中的静态方法,使用传入的元素生成一个新的 jQuery 元素,注意该方法添加了属性prevObject 保留了this指向(this代表的是匹配元素集合),这样做是为什么?其实jQuery的很多方法里面都有return this这样的写法来实现链式调用,但是实现链式调用的前提要保证每一个return this都不能具有破坏性。如果某一个方法真的要修改匹配元素集合 , 那么它就会调用 pushStack 方法 , 把之前的元素集合保存起来 , 以便以后使用 end 方法恢复。

  2. 方法中的this.constructor()表示创建当前实例的函数,也就是jQuery本身,当jQuery本身执行的时候没有传入任何参数的情况下回会返回一个空对象,详细介绍在 #23

  3. merge方法是将连个数组拼接到一起,返回一个新的数组,注意当第一个参数传入jQuery函数的返回值时,length是在原型中被定义的,而且初始值为0

  4. end方法是恢复匹配元素集合的,继续实现链式调用

来分析一下jQuery是如何进行数据类型检测的,因为jQuery中代码都写在一个自执行函数里面,通过闭包的形式来隐藏变量。关于和判断数据类型的有关的有两部分代码。

var class2type = {};
var toString = class2type.toString;
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( i, name ) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );

下面代码的往jQuery函数上拓展的静态方法

type: function( obj ) {
		if ( obj == null ) {
			return obj + "";
		}
		return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj;
	},
each: function( obj, callback ) {
		var length, i = 0;
		if ( isArrayLike( obj ) ) {
			length = obj.length;
			for ( ; i < length; i++ ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		} else {
			for ( i in obj ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		}
		return obj;
	},

通过上面的代码,我们发现jQuery将所有的数据类型都都保存在一个叫class2type 的变量中。each方法是将传入的数组或对象进行循环遍历并执行回调函数,type方法是判断数据类型的方法,我在这里详细解释一下,先判断传入进来的参数是不是nullundefined如果是就两个值就通过调用String()方法并返回,如果不是这两个值,然后再判断是不是objectfunction如果是将会把class2type 的变量存储的对应的数据类型返回,如果该变量没有对应的数据类型将会返回一个object,如果不是objectfunction类型将会继续使用typeof检测。

JavaScript没有重载

ECMAScript函数不能像传统意义上那样实现重载。而在其他语言(如Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScirpt函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。如果在ECMAScript中定义了两个名字相同的函数,则该名字只属于后定义的函数。请看下面的例子:

function addSomeNumber(num){
  return num + 100;
}
function addSomeNumber(num) {
  return num + 200;
}
var result = addSomeNumber(100);    //300

在此,函数 addSomeNumber() 被定义了两次。第一个版本给参数加 100 ,而第二个版本给参数加 200 。由于后定义的函数覆盖了先定义的函数,因此当在最后一行代码中调用这个函数时,返回的结果就是 300 。

如前所述,通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿重载。

如何实现数组去重

数组去重实现的**多种多样,下面是我在项目中使用的一些方法:

方法1

function fn1(arr) {
    return arr.filter((item, index) => !arr.slice(0, index).includes(item))
}

方法2

function fn2(arr) {
    return [...new Set(arr)]
}

方法3

function fn3(arr) {
    let newArr = arr.slice();
    for (let i = 0; i < newArr.length; i++) {
        const current = newArr[i];
        if (newArr.indexOf(current) !== newArr.lastIndexOf(current)) {
            newArr[i] = newArr[newArr.length - 1];
            i--;
            newArr.length--;
        }
    }
    return newArr;
}

方法4

function fn4(arr) {
    let newArr = [];
    for (let i = 0, len = arr.length; i < len; i++) {
        const current = arr[i];
        if (!newArr.includes(current)) {
            newArr.push(current);
        }
    }
    return newArr;
}

方法5

function fn5(arr) {
    let obj = {};
    let newArr = [];
    for (let i = 0, len = arr.length; i < len; i++) {
        const current = arr[i];
        if (obj[current] !== current) {
            obj[current] = current;
            newArr.push(current);
        }
    }
    obj = null;
    return newArr;
}

方法6

let ary = ["1", 1, 2, 2, 4, 5, NaN, NaN, undefined];

function noRepeat(ary) {
    let newAry = [];
    let obj = {};

    for (const item of ary) {
        if (!obj[item + typeof item]) {
            obj[item + typeof item] = 1;
            newAry.push(item);
        }
    }

    obj = null;
    return newAry;
}

console.log(noRepeat(ary))//[ '1', 1, 2, 4, 5, NaN, undefined ]

怎样拷贝对象(深/浅拷贝)

#说到拷贝对象,大家可能会先想到Object.assign这个方法,但是它并不是那么完美,它只会拷贝一层对象,如果对象里面还有对象它就引用了原来的对象。具体看下面代码

  1. 一层拷贝没任何问题
let obj = {
    aa: 11
}

let newObj = Object.assign({}, obj);
obj.aa = 22;

console.log(newObj.aa); //11
console.log(obj.aa); //22
  1. 多层拷贝就变成引用了
let obj = {
    aa: 11,
    bb: {
        cc: 22
    }
}

let newObj = Object.assign({}, obj);
obj.bb.cc = 33;

console.log(newObj.bb.cc); //33
console.log(obj.bb.cc); //33

像上面这样拷贝对象我们叫浅拷贝,其实我们也可以通过for-in循环来模拟浅拷贝

let obj = {
    aa: 11,
    bb: {
        cc: 22
    }
}

let newObj = {};
for (const key in obj) {
    newObj[key] = obj[key];
}

下面简单实现一个对象的深拷贝

function clone(Obj) {  
    var buf;    
    if (Obj instanceof Array) {    
        buf = [];  // 创建一个空的数组   
           
        var i = Obj.length;    
        while (i--) {      
            buf[i] = clone(Obj[i]);    
        }    
        return buf;  
    } else if (Obj instanceof Object) {    
        buf = {};  // 创建一个空对象     
          
        for (var k in Obj) {  // 为这个对象添加新的属性   
                 
            buf[k] = clone(Obj[k]);    
        }    
        return buf;  
    } else {    
        return Obj;  
    }
}

比较详细的写法可以像下面这样

let obj = {aa: 11, bb: {cc: [0, [1]]}}

function copyObj(options) {
    return copy(options)
}

function copyAry(ary) {
    let newAry = []
    for (const item of ary) {
        let value = item;
        if (Object.prototype.toString.call(value) === "[object Object]") value = copyObj(value);
        if (Object.prototype.toString.call(value) === "[object Array]") value = copyAry(value);
        newAry.push(value);
    }
    return newAry;
}

function copy(obj) {
    let newObj = {};
    for (const key in obj) {
        let value = obj[key];
        if (Object.prototype.toString.call(value) === "[object Object]") value = copyObj(value);
        if (Object.prototype.toString.call(value) === "[object Array]") value = copyAry(value);
        newObj[key] = value;
    }
    return newObj;
}


let newObj = copyObj(obj);

// 测试
obj.bb.cc[1][0] = 666;
console.log(newObj.bb.cc[1][0]) //1
console.log(obj.bb.cc[1][0]) //666

什么是面向对象(面向对象开发)

JavaScript是一种面向对象的语言。不严格地讲,这意味着我们不用全局的定义函数去操作不同类型的值,数据类型本身可以定义方法(method)来使用。例如,要对数组a中的元素进行排序,不必要将a传入sort()函数,而是调用a的一个方法sort():

a.sort();//sort(a)的面向对象的版本

面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。JavaScript有很多内置类,如NumberStringObjectFunction

深入理解原型

什么是原型

我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。

每一个对象(除null)都和另一个对象相关联,那个关联的对象就是原型对象,每一个对象都从原型中继承属性。

前端要注意的网站安全

  1. XSS

  2. sql注入

  3. CSRF:是跨站请求伪造,很明显根据刚刚的解释,他的核心也就是请求伪造,通过伪造身份提交POST和GET请求来进行跨域的攻击

完成CSRF需要两个步骤:

  1. 登陆受信任的网站A,在本地生成 COOKIE

  2. 在不登出A的情况下,或者本地 COOKIE 没有过期的情况下,访问危险网站B。

函数提升面试题

第一题

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}
}
b();
console.log(a);

输出:1

第二题

function foo(){
    function bar() {
        return 3;
    }
    return bar();
    function bar() {
        return 8;
    }
}
alert(foo());

输出:8

第三题

function parent() {
    var hoisted = "I'm a variable";
    function hoisted() {
        return "I'm a function";
    }
    return hoisted(); 
}
console.log(parent());

输出:TypeError: hoisted is not a function

第四题

alert(foo());
function foo() {
 var bar = function() {
   return 3;
 };
 return bar();
 var bar = function() {
   return 8;
 };
}

输出:3

第五题

var myVar = 'foo';
(function() {
  console.log('Original value was: ' + myVar);
  var myVar = 'bar';
  console.log('New value is: ' + myVar);
})();

输出:Original value was: undefined”, “New value is: bar

前端性能优化那些事

  1. 减少http请求次数:CSS Sprites, JS、CSS 源码压缩、图片大小控制合适;网页 Gzip,CDN 托管,data 缓存 ,图片服务器

  2. 前端模板 JS + 数据,减少由于HTML标签导致的带宽浪费,前端用变量保存 AJAX 请求结果,每次操作本地变量,不用请求,减少请求次数

  3. 用 innerHTML 代替 DOM 操作,减少 DOM 操作次数,优化 javascript 性能

  4. 当需要设置的样式很多时设置 className 而不是直接操作 style

  5. 少用全局变量、缓存DOM节点查找的结果。减少 IO 读取操作

  6. 避免使用 CSS Expression(css表达式)又称 Dynamic properties(动态属性)

  7. 图片预加载,将样式表放在顶部,将脚本放在底部,加上时间戳

手写一个promise

先了解一下Promise,Promise是通过构造函数的方式来定义,Promise有三种状态,Promise支持then方法,那么知道了这些,就简单的实现一下。

function Promise(executer) {
    // 默认的状态
    this.status = "pending";
    // 成功时的参数
    this.value = undefined;
    // 失败时的参数
    this.reason = undefined;

    let resolve = value => {
        // 用箭头函数保留this指向
        if (this.status === "pending") {
            this.status = "resolved"
            this.value = value;
        }
    }
    let reject = reason => {
        if (this.status === "pending") {
            this.status = "rejected";
            this.reason = reason;
        }
    }

    try {
        // 代码没错时执行
        executer(resolve, reject);
    } catch (error) {
        // 代码有错时执行
        reject(error);
    }
}
Promise.prototype.then = function (onFulfilled, onRejected) {
    if (this.status === "resolved") {
        // 成功时的回调
        onFulfilled(this.value)
    }
    if (this.status === "rejected") {
        // 失败时的回调
        onRejected(this.reason);
    }
}

下面比较完整的写法

function Promise(executer) {
    this.status = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.resolveCallbacks = [];
    this.rejectCallbacks = [];
    let resolve = (value) => {
        if (this.status == "pending") {
            this.value = value;
            this.status = "resolved";
            this.resolveCallbacks.forEach(item => item(this.value))
        }
    };
    let reject = (reason) => {
        if (this.status == "pending") {
            this.reason = reason;
            this.status = "rejected";
            this.rejectCallbacks.forEach(item => item(this.reason))
        }
    };
    try {
        executer(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
Promise.prototype.then = function (onFulfilled, onREjected) {
    if (this.status == "resolved") {
        return new Promise((resolve, reject) => {
            let val = onFulfilled(this.value);
            if (val instanceof Promise) {
                val.then(resolve, reject)
            } else {
                resolve(val);
            }
        })
    }
    if (this.status == "rejected") {
        return new Promise((resolve, reject) => {
            let val = onREjected(this.value);
            if (val instanceof Promise) {
                val.then(resolve, reject)
            } else {
                resolve(val);
            }
        })
    }
    if (this.status == "pending") {
        return new Promise((resolve, reject) => {
            this.resolveCallbacks.push(() => {
                let val = onFulfilled(this.value);
                if (val instanceof Promise) {
                    val.then(resolve, reject)
                } else {
                    resolve(val);
                }
            });
            this.rejectCallbacks.push(() => {
                let val = onREjected(this.value);
                if (val instanceof Promise) {
                    val.then(resolve, reject)
                } else {
                    resolve(val);
                }
            });
        })
    }
};
module.exports = Promise;

markdown编辑器

欢迎使用Markdown编辑器写博客

本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:

  • Markdown和扩展Markdown简洁的语法
  • 代码块高亮
  • 图片链接和图片上传
  • LaTex数学公式
  • UML序列图和流程图
  • 离线写博客
  • 导入导出Markdown文件
  • 丰富的快捷键

快捷键

  • 加粗 Ctrl + B
  • 斜体 Ctrl + I
  • 引用 Ctrl + Q
  • 插入链接 Ctrl + L
  • 插入代码 Ctrl + K
  • 插入图片 Ctrl + G
  • 提升标题 Ctrl + H
  • 有序列表 Ctrl + O
  • 无序列表 Ctrl + U
  • 横线 Ctrl + R
  • 撤销 Ctrl + Z
  • 重做 Ctrl + Y

Markdown及扩展

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]

使用简单的符号标识不同的标题,将某些文字标记为粗体或者斜体,创建一个链接等,详细语法参考帮助?。

本编辑器支持 Markdown Extra ,  扩展了很多好用的功能。具体请参考Github.

表格

Markdown Extra 表格语法:

项目 价格
Computer $1600
Phone $12
Pipe $1

可以使用冒号来定义对齐方式:

项目 价格 数量
Computer 1600 元 5
Phone 12 元 12
Pipe 1 元 234

###定义列表

Markdown Extra 定义列表语法:
项目1
项目2
: 定义 A
: 定义 B

项目3
: 定义 C

: 定义 D

> 定义D内容

代码块

代码块语法遵循标准markdown代码,例如:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None
class SomeClass:
    pass
>>> message = '''interpreter
... prompt'''

###脚注
生成一个脚注1.

目录

[TOC]来生成目录:

[TOC]

数学公式

使用MathJax渲染LaTex 数学公式,详见math.stackexchange.com.

  • 行内公式,数学公式为:$\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N$。
  • 块级公式:

$$ x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$

更多LaTex语法请参考 这儿.

UML 图:

可以渲染序列图:

张三->李四: 嘿,小四儿, 写博客了没?
Note right of 李四: 李四愣了一下,说:
李四-->张三: 忙得吐血,哪有时间写。

或者流程图:

st=>start: 开始
e=>end: 结束
op=>operation: 我的操作
cond=>condition: 确认?

st->op->cond
cond(yes)->e
cond(no)->op
  • 关于 序列图 语法,参考 这儿,
  • 关于 流程图 语法,参考 这儿.

离线写博客

即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。

用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。

博客发表后,本地缓存将被删除。 

用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。

**注意:**虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱

##浏览器兼容

  1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下问题
    1. 不支持离线功能
    2. IE9不支持文件导入导出
    3. IE10不支持拖拽文件导入

Footnotes

  1. 这里是 脚注内容.

严格模式和非严格模式的区别

"use strict"是ECMAScript 5引入的一条指令。指令不是语句(但非常接近于语句)。

"use strict"指令和普通的语句之间有两个重要的区别:

  • 它不包含任何语言的关键字,指令仅仅是一个包含一个特殊字符串直接量的表达式(可以是使用单引号也可以使用双引号),对于那些没有实现ECMAScript 5的JavaScript解释器来说,它只是一条没有副作用的表达式语句,它什么也没做。将来的ECMAScript标准希望将use用做关键字,这样就可以省略引号了。

  • 它只能出现在脚本代码的开始或者函数体的开始、任何实体语句之前。但它不必一定出现在脚本的首行或函数体内的首行,因为"use strict"指令之后或之前都可能有其他字符串直接量表达式语句,并且JavaScript的具体实现可能将它们解析为解释器自有的指令。在脚本或者函数体内第一条常规语句之后字符串直接量表达式语句只当做普通的表达式语句对待;它们不会当做指令解析,它们也没有任何副作用。

  • 使用"use strict"指令的目的是说明(脚本或函数中)后续的代码将会解析为严格代码(strict code)。如果顶层(不在任何函数内的)代码使用了"use strict"指令,那么它们就是严格代码。如果函数体定义所处的代码是严格代码或者函数体使用了"use strict"指令,那么函数体的代码也是严格代码。如果eval()调用时所处的代码是严格代码或者eval()要执行的字符串中使用了"scrict code"指令,则eval()内的代码是严格代码。严格代码以严格模式执行。ECMAScript 5中的严格模式是该语言的一个受限制的子集,它修正了语言的重要缺陷,并提供健壮的查错功能和增强的安全机制。严格模式和非严格模式之间的区别如下(前三条尤为重要):

  • 在严格模式中禁止使用with语句。

  • 在严格模式中,所有的变量都要先声明,如果给一个未声明的变量、函数、函数参数、catch从句参数或全局对象的属性赋值,将会抛出一个引用错误异常(在非严格模式中,这种隐式声明的全局变量的方法是给全局对象新添加一个新属性)。

  • 在严格模式中,调用的函数(不是方法)中的一个this值是undefined。(在非严格模式中,调用的函数中的this值总是全局对象)。可以利用这种特性来判断JavaScript实现是否支持严格模式:

  • 同样,在严格模式中,当通过call()或apply()来调用函数时,其中的this值就是通过call()或apply()传入的第一个参数(在非严格模式中,null和undefined值被全局对象和转换为对象的非对象值所代替)。

  • 在严格模式中,给只读属性赋值和给不可扩展的对象创建新成员都将抛出一个类型错误异常(在非严格模式中,这些操作只是简单地操作失败,不会报错)。

  • 在严格模式中,传入eval()的代码不能在调用程序所在的上下文中声明变量或定义函数,而在非严格模式中是可以这样做的。相反,变量和函数的定义是在eval()创建的新作用域中,这个作用域在eval()返回时就弃用了。

var hasStrictMode=(function(){"use strict";return this===undefined}());
  • 在严格模式中,函数里的arguments对象拥有传入函数值的静态副本。

  • 在非严格模式中,arguments对象具有“魔术般”的行为,arguments里的数组元素和函数参
    数都是指向同一个值的引用。

  • 在严格模式中,当delete运算符后跟随非法的标识符(比如变量、函数、函数参数)
    时,将会抛出一个语法错误异常(在非严格模式中,这种delete表达式什么也没做,并返回
    false)。

  • 在严格模式中,试图删除一个不可配置的属性将抛出一个类型错误异常(在非严格模式中,delete表达式操作失败,并返回false)。

  • 在严格模式中,在一个对象直接量中定义两个或多个同名属性将产生一个语法错误(在非严格模式中不会报错)。

  • 在严格模式中,函数声明中存在两个或多个同名的参数将产生一个语法错误(在非严格模式中不会报错)。

  • 在严格模式中是不允许使用八进制整数直接量(以0为前缀,而不是0x为前缀)的(在非严格模式中某些实现是允许八进制整数直接量的)。

  • 在严格模式中,标识符eval和arguments当做关键字,它们的值是不能更改的。不能给这些标识符赋值,也不能把它们声明为变量、用做函数名、用做函数参数或用做catch块的标识符。

  • 在严格模式中限制了对调用栈的检测能力,在严格模式的函数中,arguments.callerarguments.callee都会抛出一个类型错误异常。严格模式的函数同样具有callerarguments属性,当访问这两个属性时将抛出类型错误异常(有一些JavaScript的实现在非严格模式里定义了这些非标准的属性)。

CSS高级技巧

超出文本以...的形式显示

white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;

提示:当以上三个属性和display: flexdisplay: grid属性结合使用时无效。

background-clip 属性规定背景的绘制区域。
background-origin 属性规定 background-position 属性相对于什么位置来定位。


绘制一个正方形

width: 100%;
padding-top: 100%;

提示:当没有设置height属性是padding属性是根据元素的width计算。


绘制小三角

color: #000;
width: 0;
height: 0;
border-top: 10px solid;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid transparent;

提示:当border没有指定颜色就是字体的颜色

什么是闭包?

闭包是指有权访问另一个函数作用域中的变量的函数。

JavaScript基本概念

JavaScript的类型可以分为原始类型和对象类型,也可分为可以拥有方法的类型和不能拥有方法的类型,同样可分为可变(mutable)类型和不可变(immutable)类型。可变类型的值是可修改的。

JavaScript可以自由地进行数据类型转换。比如,如果在程序期望使用字符串的地方使用了数字,JavaScript会自动将数字转换为字符串。如果在期望使用布尔值的地方使用了非布尔值,JavaScript也会进行相应的转换。

果函数用来初始化(使用new运算符)一个新建的对象,我们称之为构造函数(constructor)。每个构造函数定义了一类(class)对象——由构造函数初始化的对象组成的集合。类可以看做是对象类型的子类型。

JavaScript变量是无类型的(untyped),变量可以被赋予任何类型的值,同样一个变量也可以重新赋予不同类型的值。

JavaScript不区分整数值和浮点数值。

JavaScript语言核心包括Date()构造函数,用来创建表示日期和时间的对象。这些日期对象的方法为日期计算提供了简单的API。

JavaScript的内置功能之一就是字符串连接。如果将加号(+)运算符用于数字,表示两数相加。但将它作用于字符串,则表示字符串连接,将第二个字符串拼接在第一个之后。

记住,在JavaScript中字符串是固定不变的,类似replace()和toUpperCase()的方法都返回新字符串,原字符串本身并没有发生改变。

在ECMAScript 5中,字符串可以当做只读数组,除了使用charAt()方法,也可以使用方括号来访问字符串中的单个字符。

JavaScript定义了RegExp()构造函数,用来创建表示文本匹配模式的对象。这些模式称为“正则表达式”(regular expression),JavaScript采用Perl中的正则表达式语法。

布尔值包含toString()方法,因此可以使用这个方法将字符串转换为"true"或"false"

&&”运算符执行了逻辑与(AND)操作。当且仅当两个操作数都是真值时它才返回true;否则返回false。“||”运算符是布尔或(OR)操作,如果两个操作数其中之一为真值它就返回true,如果两个操作数都是假值则返回false。最后,一元操作符“!”执行了布尔非(NOT)操作:如果操作数是真值则返回false;如果是假值,则返回true。

对null执行typeof预算,结果返回字符串"object",也就是说,可以将null认为是一个特殊的对象值,含义是“非对象”。

当JavaScript解释器启动时(或者任何Web浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:

  • 全局属性,比如undefined、Infinity和NaN。
  • 全局函数,比如isNaN()、parseInt()(见3.8.2节)和eval()
  • 构造函数,比如Date()、RegExp()、String()、Object()和Array()
  • 全局对象,比如Math和JSON

JavaScript对象是一种复合值:它是属性或已命名值的集合。通过“.”符号来引用属性值。当属性值是一个函数的时候,称其为方法。通过o.m()来调用对象o中的方法。

字符串既然不是对象,为什么它会有属性呢?只要引用了字符串s的属性,JavaScript就会将字符串值通调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁。

ull和undefined没有包装对象:访问它们的属性会造成一个类型错误。

字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。

原始值的比较是值的比较:只有在它们的值相等时它们才相等。

我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。依照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。

Number类定义的toString()方法可以接收表示转换基数(radix)的可选参数,如果不指定此参数,转换规则将是基于十进制。同样,亦可以将数字转换为其他进制数。

Number类为这种数字到字符串的类型转换场景定义了三个方法。toFixed()根据小数点后的指定位数将数字转换为字符串,它从不使用指数记数法。toExponential()使用指数记数法将数字转换为指数形式的字符串,其中小数点前只有一位,小数点后的位数则由参数指定(也就是说有效数字位数比指定的位数要多一位),toPrecision()根据指定的有效数字位数将数字转换成字符串。

parseInt()可以接收第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~36

JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。

一个变量的作用域(scope)是程序源代码中定义这个变量的区域。

当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说这个变量无法通过delete运算符删除。可能你已经注意到了,如果你没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配值属性,并可以删除它们。

每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程称做“变量解析”(variable resolution)),它会从链中的第一个对象开始查找。

表达式(expression)JavaScript中的一个短语,JavaScript解释器会将其计算(evaluate)出一个结果。

左值(lvalue)是一个古老的术语,它是指“表达式只能出现在赋值运算符的左侧”。在JavaScript中,变量、对象属性和数组元素均是左值。

尽管函数声明语句和函数定义表达式包含相同的函数名,但二者仍然不同。两种方式都创建了新的函数对象,但函数声明语句中的函数名是一个变量名,变量指向函数对象。和通过var声明变量一样,函数定义语句中的函数被显式地“提前”到了脚本或函数的顶部。因此它们在整个脚本和函数内都是可见的。使用var的话,只有变量声明提前了——变量的初始化代码仍然在原来的位置。然而使用函数声明语句的话,函数名称和函数体均提前:脚本中的所有函数和函数中所有嵌套的函数都会在当前上下文中其他代码之前声明。也就是说,可以在声明一个JavaScript函数之前调用它。

在执行for/in语句的过程中,JavaScript解释器首先计算object表达式。如果表达式为null或者undefined,JavaScirpt解释器将会跳过循环并执行后续的代码 。如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象,其实,for/i n循环并不会遍历对象的所有属性,只有“可枚举”(enumerable)的属
性才会遍历到。

单独使用break语句的作用是立即退出最内层的循环或switch语句。

return语句让解释器跳出函数体的执行,并提供本次调用的返回值。return语句只能在函数体内出现,如果不是的话会报语法错误。

continue语句和break语句非常类似,但它不是退出循环,而是转而执行下一次循环。

try/catch/finally语句是JavaScript的异常处理机制。

exports和module.exports的区别

先理解如果没有导出文件直接引入文件会发生什么?

导出文件

//A.js代码

function fn() {
    console.log("导出函数")
}
console.log(1)

导入文件

//B.js代码

const fn = require("./A")

当执行B文件的时候回输出1,这是为什么呢?

在nodeJS中模块是基于commonJS规范实现,实现原理是文件读写,每个文件都是一个模块,文件中的代码会默认写在一个闭包中,所以当你引入A.js其实是执行的如下代码:

const fn = (function () {
    function fn() {
        console.log("导出函数")
    }
    console.log(1)
})()

这就是文件导入会经历那些过程

接下来分析一下文件导出的过程

(function (exports, require, module, __filename, __dirname) {
    module.exports = exports = this = {};
    function fn() {
        console.log("导出函数");
    }
    console.log(1);
    return module.exports;
})()

文件导出默认返回的module.exports,所以有时候默认会这样导出文件exports.fn=fn或者 module.exports={fn}导出多个。

函数内部的this

  • 普通函数,this引用的是函数执行的环境对象
  • 箭头函数,this引用的是函数定义的环境对象
  • 构造函数,this引用的是构造函数的实例对象

手写一个jsonp

//根据指定的URL发送一个JSONP请求
//然后把解析得到的响应数据传递给回调函数
//在URL中添加一个名为jsonp的查询参数,用于指定该请求的回调函数的名称
function getJSONP(url, callback) { //为本次请求创建一个唯一的回调函数名称
    var cbnum = "cb" + getJSONP.counter++; //每次自增计数器
    var cbname = "getJSONP." + cbnum; //作为JSONP函数的属性
    //将回调函数名称以表单编码的形式添加到URL的查询部分中
    //使用jsonp作为参数名,一些支持JSONP的服务
    //可能使用其他的参数名,比如callback
    if (url.indexOf("?") === -1) //URL没有查询部分
        url += "?jsonp=" + cbname; //作为查询部分添加参数
    else //否则
        url += "&jsonp=" + cbname; //作为新的参数添加它
    //创建script元素用于发送请求
    var script = document.createElement("script"); //定义将被脚本执行的回调函数
    getJSONP[cbnum] = function (response) {
        try {
            callback(response); //处理响应数据
        } finally { //即使回调函数或响应抛出错误
            delete getJSONP[cbnum]; //删除该函数
            script.parentNode.removeChild(script); //移除script元素
        }
    }; //立即触发HTTP请求
    script.src = url; //设置脚本的URL
    document.body.appendChild(script); //把它添加到文档中
}
getJSONP.counter = 0; //用于创建唯一回调函数名称的计数器

jQuery源码阅读三

jQuery原型中的方法使用的是jQuery中的静态方法,所以他们共用同一个each方法

jQuery.fn = jQuery.prototype = {
  each: function( callback ) {
		return jQuery.each( this, callback );
	}
}

上面是原型上的代码,下面是静态方法的代码

jQuery.extend( {
  each: function( obj, callback ) {
  var length, i = 0;

  if ( isArrayLike( obj ) ) {
    length = obj.length;
    for ( ; i < length; i++ ) {
      if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
        break;
      }
    }
  } else {
    for ( i in obj ) {
      if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
        break;
      }
    }
  }}
})

代码中的isArrayLike是判断是不是一个类数组

jQuery原型中的方法

  1. toArray:把匹配的元素集合转换为数组
  2. get:通过传入进来的索引来获取指定索引的元素(返回一个普通元素)
  3. pushStack:通过传入一个普通元素变成一个和jQuery相关的元素
  4. each:会调用JQuery函数中静态的each方法,该方法是通过循环遍历数组或对象中的每一项并执行回调函数(返回的是环遍历数组或对象)
  5. map:会调用JQuery函数中静态的map方法,该方法是通过循环遍历数组或对象中的每一项并执行回调函数(返回执行每一次回调函数的执行结果组成的数组)
  6. slice:和数组的slice方法类似
  7. first:获取匹配元素集合中的第一项
  8. last:获取匹配元素集合中的最后项
  9. eq:通过传入进来的索引来获取指定索引的元素(返回一个jQuery元素)
  10. end:返回之前的匹配元素集合

往jQuery本身拓展的的方法也叫静态方法

  1. error:抛出一个错误
  2. noop:一个空函数
  3. isFunction:判断是不是一个函数
  4. isWindow:判断对象中是否有window属性
  5. isNumeric:判断是不是可计算的值
  6. isPlainObject:判断是不是普通对象
  7. isEmptyObject:判断是不是空对象
  8. type:检测数据类型
  9. globalEval:执行代码
  10. each:循环遍历数据或对象并执行回调函数
  11. trim:去除首尾空格
  12. makeArray:转换为数组
  13. inArray:判断数中有没有某个元素
  14. merge:将连个数组拼接到一起
  15. map:循环遍历数据或对象并执行回调函数

不能错过的前端资料!

剖析一下MVVM原理

什么是MVVM

MVVM叫双向数据绑定,数据影响视图,视图影响数据。

Angular:脏值检测
Vue:数据劫持+发布订阅

实现核心Object.defineProperty

const vue = new Vue({
    el: "#app",
    data: {
        a: {
            a: 1
        }
    }
})
function Vue(options = {}) {
    this.$options = options;
    var data = this._data = this.$options.data;
    observe(data);
    // this代理this._data
    for (const key in data) {
        Object.defineProperty(this, key, {
            enumerable: true,
            get() {
                return this._data[key];
            },
            set(newValue) {
                this._data[key] = newValue;
            }
        })
    }
}

function observe(data) {
    if(Object.prototype.toString.call(data) !== "[object Object]") return;
    return new Observe(data);
}

function Observe(data) {
    for (const key in data) {
        let value = data[key];
        observe(value);
        // 深度响应原理:每次赋予一个新对象的时候都会增加数据劫持
        Object.defineProperty(data, key, {
            enumerable: true,
            get() {
                return value;
            },
            set(newValue) {
                if (newValue === value) return;
                value = newValue;
                observe(newValue);
            }
        })
    }
}

说一下字符串都有哪些方法

charAt()

取出一个字符串中指定位置的字符。

charCodeAt()

返回一个字符串中指定位置的字符的编码。

🆕 codePointAt()

ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

concat()

将一个或多个值连接成一个字符串。

indexOf()

在指定字符串中寻找一个字符或子串。

lastIndexOf()

在指定字符串中向后寻找一个字符或子串。

localeCompare()

使用本地定义的顺序比较字符串。

match()

使用正则表达式执行模式匹配。

replace()

使用正则表达式执行查找与替换操作。

search()

在一个字符串中查找匹配某个正则表达式的子串。

slice()

返回字符串的一个切片或子串。

split()

在指定的分隔符字符串或正则表达式处断开,将一个字符串分割为由字符串组成的数组。

substr()

提取字符串的一个子串,substring()的一个变体。

substring()

提取字符串的一个子串。

toLowerCase()

返回指定字符串的一份副本,其中所有的字符都已转换为小写。

toString()

返回原始的字符串值。

toUpperCase()

返回指定字符串的一份副本,其中所有的字符都已转换为大写。

trim()

返回指定字符串的一份副本,其中前后的空白字符都已删除。

valueOf()

返回原始的字符串值。

🆕 includes()

返回布尔值,表示是否找到了参数字符串。

🆕 startsWith()

返回布尔值,表示参数字符串是否在原字符串的头部。

🆕 endsWith()

返回布尔值,表示参数字符串是否在原字符串的尾部。

🆕 padStart()

padStart()用于头部补全

🆕 padEnd()

padEnd()用于尾部补全

静态方法

String.fromCharCode()

使用作为参数传入的字符编码创建一个新的字符串。

🆕 String.fromCodePoint()

ES6 提供了String.fromCodePoint方法,可以识别大于0xFFFF的字符

水平垂直居中一个元素

方法1

position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto;

方法2

position: absolute;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);

方法3

position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
width: 100px;
height: 100px;

ES5和ES6的区别

在一个执行环境中没有用任何关键声明变量就直接赋值,浏览器会在全局执行环境中(window)定义一个相同属性名的属性并为其赋值

JavaScript中中的变量的无类型的,只要声明一个变量,就可以为其赋任何数据类型的值

var:有变量提示,可以重复声明
let:没有变量提升,不可以重复声明
const:没有变量提升,不可以重复声明

function:定义函数,有变量提升(除块级作用域之外)
calss:定义类,没有变量提升

ES6新增一中数据类型Symbol

学习webpack配置

  1. webpack默认的配置文件名叫webpack.config.js,一般情况要自己创建。(运行webpack的时候回自动寻找webpack.config.js文件)
  2. 如果文件名不是webpack.config.js,运行时需要指定配置文件webpack --config 配置文件名,在Vue中看到webpack.dev.config.js可以使用webpack --config webpack.dev.config.js运行
  3. 可以在config.js中的script配置命令,使用npm run 属性名
  4. 配置文件webpack.config.js需要导出一个对象,使用module.exports={},对象里面写详细的配置。
  • entry 入口(打包的入口从哪个文件开始)
  • output 出口(打包以后的文件输出目录)

六种继承

原型链继承

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
}
//B继承A
B.prototype = new A();
B.prototype.constructor = B;

利用原型让一个引用类型继承另一个引用类型的属性和方法

借用构造函数继承

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
    //B继承A
    A.call(this);
}

在子类构造函数的内部调用父类构造函数

组合继承(原型+借用构造函数)

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
    //B继承A
    A.call(this);
}
//B继承A
B.prototype = new A();
B.prototype.constructor = B;

原型式继承

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
}
//B继承A
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

借助原型可以基于已有的对象创建新的对象

寄生式继承

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
    //B继承A
    let obj = new A;
    for (const key in obj) {
        this[key] = obj[key]
    }
    obj = null;
}

在子类的构造函数中增加父类的属性和方法

寄生组合式继承(推荐)

function A() {
    this.a = "a";
}

function B() {
    this.b = "b";
    //B继承A
    let obj = new A;
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            this[key] = obj[key]
        }
    }
    obj = null;
}
//B继承A
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

通过构造函数来继承属性,通过原型链来继承方法

ES6中的继承

class A {
    constructor() {
        this.a = "a"
    }
}
class B extends A {
    //B继承A
}

defineProperty详解

  1. mvvm双向数据绑定,数据影响视图,视图影响数据,数据劫持+发布订阅模式
  2. angle脏值检测
let obj = {};
Object.defineProperty(obj, "test", {
    value: "vue"
})
console.log(obj)//{test: "vue"}

不可配置

let obj = {};
Object.defineProperty(obj, "test", {
    value: "vue"
})
delete obj.test;
console.log(obj)//{test: "vue"}

可配置

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    value: "vue"
})
delete obj.test;
console.log(obj)//{}

不可枚举

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    value: "vue"
})
for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
        const value = obj[key];
        console.log(value)
    }
}

可枚举

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    enumerable: true,
    value: "vue"
})
for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
        const value = obj[key];
        console.log(value) //vue
    }
}

不可写

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    enumerable: true,
    value: "vue"
})
obj.test = "change"
console.log(obj) //{test: "vue"}

可写

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    enumerable: true,
    writable: true,
    value: "vue"
})
obj.test = "change"
console.log(obj) //{test: "change"}

getter

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    enumerable: true,
    // writable: true,
    // value: "vue"

    get () {
        return "vue";
    }
})
console.log(obj.test) //vue

setter

let obj = {};
Object.defineProperty(obj, "test", {
    configurable: true,
    enumerable: true,
    // writable: true,
    // value: "vue"

    get () {
        return "vue";
    },
    set (value) {
        console.log(value);
    }
})
obj.test = "change"; //change

说一下变量提升?

  • 作用域形成之后,代码执行之前,先把所有带var和function关键字的提前声明或者定义,一起情况下,var是只声明不定义,function既声明又会定义
  • let没有变量提升,不可以重复声明,全局下声明的变量不会给window增加属性,
  • const没有变量提升,不可以重复声明,全局下声明的变量不会给window增加属性,一旦声明必须赋值,一般情况下,赋值之后不可修改
  • 变量提升的几种特殊情况
  • 自执行函数不进行变量提升
  • 函数当作参数传入时不进行变量提升
  • return出来的内容执行,但不进行变量提升,return后面的内容不执行,但是需要经行变量提升
  • if条件判断的时候不管条件是否成立,都会经行变量提升,但var和function都是只声明不定义,如果条件成立,第一步会先给函数赋值

前端开发必会的面试题

前端 | 请你谈谈Cookie的弊端
前端 | 浏览器本地存储
前端 | web storage和cookie的区别
前端 | display:none和visibility:hidde...
前端 | CSS中 link 和@import 的区别是?
前端 | position的absolute与fixed共同点与不同点
前端 | 介绍一下CSS的盒子模型?
前端 | CSS 选择符、属性继承、优先级算法以及CSS3新增伪类
前端 | display 的作用以及 position 的取值的区别
前端 | CSS3有哪些新特性?
前端 | 为什么要初始化CSS样式。
前端 | 对BFC规范的理解
前端 | CSS sprites 的理解及使用
前端 | HTML 语义化
前端 | Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?
前端 | Doctype文档类型
前端 | HTML与XHTML的区别
前端 | html兼容性问题
前端 | CSS 浮动
前端 | 浮动元素引起的问题和解决办法
前端 | IE 8以下版本的浏览器中的盒模型有什么不同
前端 | DOM操作
前端 | html5 的新特性以及新标签的浏览器兼容问题
前端 | iframe的优缺点
前端 | 如何实现浏览器内多个标签页之间的通信
前端 | webSocket如何兼容低浏览器
前端 | 线程与进程的区别
前端 | 你如何对网站的文件和资源进行优化?
前端 | 请说出三种减少页面加载时间的方法
前端 | 测试代码性能的工具
前端 | 什么是 FOUC(无样式内容闪烁)?你如何来避免 FOUC?
前端 | null和undefined的区别
前端 | new操作符
前端 | 对JSON 的了解
前端 | js延迟加载的方式
前端 | 解决跨域问题
前端 | documen.write和 innerHTML 的区别
前端 | .call() 和 .apply() 的作用
前端 | 内存泄漏
前端 | 判断当前脚本运行在浏览器还是 node 环境中
前端 | 优雅降级和渐进增强
前端 | Node的优点和缺点
前端 | 对前端界面工程师职位的理解以及如何看待它的前景
前端 | 性能优化
前端 | http状态码
前端 | 页面加载过程
前端 | 平时如何管理你的项目
前端 | 最近最流行的一些东西,常去的哪些网站
前端 | javascript对象的几种创建方式
前端 | javascript继承的 6 种方法
前端 | ajax 的过程
前端 | 异步加载和延迟加载
前端 | 前端的安全问题
前端 | ie 各版本和 chrome 可以并行下载多少个资源
前端 | javascript里面的继承怎么实现,如何避免原型链上面...
前端 | 代码压缩的用法
前端 | Flash、Ajax 的优缺点
前端 | JavaScript 的同源策略
前端 | 为什么要有同源限制?
前端 | 什么是 "use strict"
前端 | GET和POST的区别
前端 | css 阻塞和 js 阻塞
前端 | eval 的作用
前端 | 写一个通用的事件侦听器函数
前端 | Node.js 的适用场景
前端 | JavaScript 原型及原型链
前端 | 怎么重构页面
前端 | WEB应用从服务器主动推送Data到客户端有那些方式?
前端 | 事件、IE与火狐的事件机制有什么区别? 如何阻止冒泡?
前端 | Ajax 是什么?Ajax 的交互模型?同步和异步的区别?...
前端 | js 对象的深度克隆代码实现
前端 | 对网站重构的理解
前端 | 如何获取UA
前端 | js 数组去重
前端 | HTTP状态码
前端 | cache-control
前端 | js 操作获取和设置 cookie

alert(1&&2),alert(0||1)
mouseenter和mouseover的区别
实现字符串的trim方法

 var a=4;
function b() {
     a=3; 
     console.log(a); 
     function a(){};
}
b();
var baz=3;
var bazz={  
    baz: 2, 
    getbaz: function() { 
         return this.baz 
    }
}
console.log(bazz.getbaz())
var g=bazz.getbaz;
console.log(g()) ;
var arr=[1,2,3,4,5];
for(var i=0;i<arr.length;i++){   
   arr[i]=function(){
   alert(i)
   }
}
arr[3]();

写出position不同值和区别
写一个div+css布局,左边图片右边文字,文字环绕图片,外面容器固定宽度,文字不固定
repaint是某个DOM元素进行重绘;reflow是整个页面进行重排

前端练习题

exercises

练习题

清除浮动的方法

  1. 给浮动元素的祖先元素设置:height:
  2. 给浮动元素的祖先元素设置:overflow: hidden;
  3. 给浮动元素的祖先元素设置:overflow: auto;
  4. 给需要清除浮动的元素设置:clear: both;
  5. 在浮动元素和需要清除浮动的元素之间添加一个元素并且设置:clear: both;
  6. 给浮动元素的祖先元素的伪类设置:display: block;content: "";clear: both;

数组toString()的原理是数组的每一项都会调用toString()

var ary = [[[1,2]],[[3,4]]];
console.log(ary.toString()) //"1,2,3,4"

对象存储值的过程

var obj = {
    name: "Qiang",
    fn: (function () {
        return obj.name
    })()
}
console.log(obj.fn);// 报错,对象是先存储值,然后再把内存地址赋值给obj

构造函数创建函数的参数

var str = "[1,2,3,4,5]";
var res = new Function("return" + str)();
console.log(res) //[1,2,3,4,5]

函数的length属性

function fn(x,y=2,z){}
console.log(fn.length); //1

对象存储值的过程

var obj = {
    name: "Qiang",
    fn: (function () {
        return obj.name;
    })()
}
console.log(obj.fn); //报错

块级作用域中变量名不能和函数名同名

console.log(AA); //undefined
{
    var AA = 10;
    function AA() {}
}
console.log(AA) //报错

关键字定义的区别

关键字 是否会给window增加属性 是否可以重复声明 是否声明时候必须赋值 是否赋值之后可以修改 是否只在自己的块级作用域里有效
var 是:white_check_mark: 是:white_check_mark: 是:white_check_mark:
let 是:white_check_mark: 是:white_check_mark:
const 是:white_check_mark: 是:white_check_mark:

变量提升提升情况

情况 var是否会声明 var是否会定义 function是否会声明 function是否会定义
全局作用域 是:white_check_mark: 是:white_check_mark: 是:white_check_mark:
if判断/for循环(不管条件是否成立) 是:white_check_mark: 是:white_check_mark:
return返回值
返回值之后的内容 是:white_check_mark: 是:white_check_mark: 是:white_check_mark:

爱奇艺面试题

var m= 1, j = k = 0; 
function add(n) { 
    return n = n+1; 
} 
y = add(m); 
function add(n) { 
    return n = n + 3; 
} 
z = add(m); 
console.log(y + "," + z);

答案:4,4

var n=2.toString();
console.log(typeof(n));

答案:报错

正则对分组的处理和贪婪性处理

var reg = /(\w)+/g;
console.log(reg.exec("wuxianqiang"))
//[ 'wuxianqiang', 'g', index: 0, input: 'wuxianqiang' ]
var reg = /(\w+)/g;
console.log(reg.exec("wuxianqiang"))
//[ 'wuxianqiang', 'wuxianqiang', index: 0, input: 'wuxianqiang' ]
var reg = /(\w+)(\d+)/g;
console.log(reg.exec("hello2018"))
//[ 'hello2018', 'hello201', '8', index: 0, input: 'hello2018' ]
var reg = /(\w+?)(\d+)/g;
console.log(reg.exec("hello2018"))
//[ 'hello2018', 'hello', '2018', index: 0, input: 'hello2018' ]

函数参数的默认值会影响函数的length属性,如果指定了默认值,则默认值前面的才属于length

function fn(a,b=1,c) {
    console.log(fn.length); //1
}
fn()

多个call的问题

function fn1() {
    console.log(1,this);
}
function fn2() {
    console.log(2,this);
}
let obj = {}
fn1.call.call.call(fn2,obj) //=>fn2.call(obj)

怎样让字符串反过来

console.log("hello".split("").reverse().join(""))

BFC背后的原理

BFC就是在一定区域内,进行单独渲染,互不影响。(标准说法:块级格式化范围)

1. 怎样可以形成一个BFC?

float: left;
float: right;
overflow: hidden;
position: absolute;

2. BFC有什么作用?

前一个元素浮动了,后一个元素没有浮动,两个元素会有重叠的问题,这是因为这两个元素都在同一个DFC中,所以只要后一个元素形成一个单独的BFC就不会受到影响,如overflow: hidden;

形成一个BFC,里面的内容就有高度了,当两两个元素都浮动的时候,外面的元素不能被里面的元素撑出高度,那么就可以给外面的元素形成一个DFC就可以,如overflow: hidden;

DFC与BFC之间不会有任何影响,当连个元素都设置里上下margin值得时候,发现连个元素之间只有一个margin了,也叫margin塌陷问题,这事只要个连个元素都单独形成一个BFC即可,就不会有问题塌陷的问题了,如overflow: hidden;

如何实现数组排序

说到数组排序,大家可能会想到数组的sort方法,使用如下

let ary = [9,6,8,3,];
ary.sort((a,b) => a - b);
console.log(ary); //[ 3, 6, 8, 9 ]

但是面试的时候难免要自己手写一个数字排序的方法,那我们毫不犹豫的写吧

        Array.prototype.bubbleSort = function bubbleSort() {
            var temp = null;
            for (var i = 0; i < this.length - 1; i++) {
                for (var k = 0; k < this.length - 1 - i; k++) {
                    if (this[k] > this[k + 1]) {
                        temp = this[k];
                        this[k] = this[k + 1];
                        this[k + 1] = temp;
                    }
                }
            }
            return this;
        }

JavaScript面试题

第一题

var a = 2;
var func = (function () {
    var a = 3;
    return function () {
        a++;
        console.log(a);
    }
})();
func();
func();

第二题

console.log(1 && 2);
console.log(1 || 2);

第三题

window.val = 1;
var json = {
    val: 10,
    dbl: function () {
        this.val *= 2;
    }
}
json.dbl();
var dbl = json.dbl;
dbl();
json.dbl.call(window);
console.log(window.val + json.val);

第四题

(function () {
    var val = 1;
    var json = {
        val: 10,
        dbl: function () {
            val *= 2;
        }
    };
    json.dbl();
    console.log(json.val + val);
}());

第五题

var foo = "hello";
(function () {
    var bar = "world";
    console.log(foo + bar);
})();
console.log(foo + bar);

第六题

var foo = {
    n: 1
};
var bar = foo;
foo.x = foo = {
    n: 2
};
console.log(foo.x);

题七题

console.log("one");
setTimeout(() => {
    console.log("two");
}, 0);
console.log("three");

第八题

var a = 1;
function func() {
    console.log(a);
    var a = "in function"
}
func();
console.log(a);

第九题

(function () {
    var a = b = "res";
})();
console.log(b);
console.log(a);

第十题

function func1() {
    var n = 99;
    nAdd = function () {
        this.n += 1;
        console.log(this.n);
    };

    function func2() {
        console.log(n);
    }
    return func2;
}
var result = func1();
result();
nAdd();
result();

第11题

var obj = {
    name: "obj",
    dose: function () {
        this.name = "dose";
        return function () {
            return this.name;
        }
    }
}
console.log(obj.dose().call(this));

第12题

var myObject = {
    foo: "bar",
    func: function () {
        var self = this;
        console.log("outer func | this.foo" + this.foo);
        console.log("outer func | self.foo" + self.foo);
        (function () {
            console.log("inner func | this.foo" + this.foo);
            console.log("inner func | self.foo" + self.foo);
        })();
    }
};
myObject.func();

第13题

var k = c = 0;
function a(n) {
    return n ? (n - 1) * a(n - 1) : n;
    k++;
    c++;
    if (c > 10) return c;
}
console.log(a(5))
console.log(k)
console.log(c)

第14题

function cc(i) {
    if (i == 3) {
        i += 2
    };
    if (i == 5) {
        i--
    };
    if (i == 4) {
        i -= 2
    };
    return i;
}

for (var i = 2; i < 6; i++) {
    console.log(cc(i))
}

第15题

var fullname = "John";
var obj = {
    fullname: "Colin",
    prop: {
        fullname: "Aurelio",
        getFullname: function () {
            return this.fullname;
        }
    }
};
console.log(obj.prop.getFullname());
var test = obj.prop.getFullname;
console.log(test());

第16题

var x = 1,
    y = z = 0;

function add(n) {
    return n = n + 2;
}
y = add(x);
console.log(y); //4

function add(n) {
    return n = n + 3;
}
z = add(x);
console.log(z);

第17题

(function () {
    var x = y = z = 1;
}())
console.log(x);
console.log(y);
console.log(z);

第18题

function Foo() {
    var i = 0;
    return function () {
        console.log(i++);
    }
}
var f1 = Foo(),
    f2 = Foo();
f1();
f1();
f2();

第19题

function test() {
    console.log(a); //undefined
    console.log(foo());
    var a = 1;

    function foo() {
        return 2;
    }
}
test();

第20题

var obj = {
    escape: function (str) {
        return str.replace("a", "b");
    },
    func: function () {
        for (var i = 0; i < 3; i++) {
            var str = "a";
            setTimeout(function () {
                str = str + "a";
                console.log(this.escape(str));
            }, i * 1000);
        }
    }
}
obj.func();

第21题

function t(a) {
    var a = "hello";
    console.log(a);

    function a() {
        console.log(null);
    }
    console.log(a);
}
t(null);

第21题

function fn(b) {
    console.log(b);

    function b() {
        console.log(b);
    }
    b();
}
fn(10);

第22题

function a(b) {
    console.log(b);
    b = function () {
        console.log(b);
    };
    b();
}
a();

第23题

var a = 1;

function b() {
    var a = 2;

    function c() {
        console.log(a);
    }
    return c;
}
b()();

第24题

var a = 100;
function test() {
    console.log(a);
    a = 10;
    console.log(a);
}
test();
console.log(a);

第25题

var a = 100;
function test() {
    console.log(a);
    var a = 10;
    console.log(a);
}
test();
console.log(a);

第26题

var a = 10;

function test() {
    a = 100
    console.log(a);
    console.log(this.a)
    var a;
    console.log(a);
}
test();
console.log(a);

第27题

var a = (function () {
    var i = 0;
    return {
        aa: function () {
            i++;
            console.log(i);

        },
        bb: function (i) {
            console.log(i);
        }
    }
})()
console.log(a)
console.log(a.bb(5))
console.log(a.bb)

第28题

var TestFunc = function () {
    console.log(1);
    for (var i = 0; i < 5; i++) {
        var t = i;
        setTimeout(function () {
            console.log(t);
        }, i * 100)
    }
    console.log(2);
}
TestFunc();

第29题

var TestFunc = function () {
    console.log(1);
    for (var i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log(i);
        }(), i * 100)
    }
    console.log(2);
}
TestFunc();

第30题

var obj = {
    name: "AAA"
};
var testFun = function (obj) {
    var newObj = obj;
    newObj.name = "BBB";
    newObj.type = "new";
    console.log(newObj);
    console.log(obj);
}
console.log(obj);
testFun(obj);
console.log(obj);

第31题

var a = 3;
(function () {
    console.log(++a);
    var a = 5;
    console.log(++a);
})()

第32题

for (var i = 0; i < 10; i++) {
    setTimeout(function () {
        console.log(i);
    }, 0);
}

第33题

var z = 10;

function foo() {
    console.log(z);
}
(function (funArg) {
    var z = 20;
    funArg();
})(foo);

第34题

var data = [];
for (var k = 0; k < 3; k++) {
    data[k] = function () {
        console.log(k);
    };
}
data[0]();
data[1]();
data[2]();

第35题

function fun(n, o) {
    console.log(o);
    return {
        fun: function (m) {
            return fun(m, n);
        }
    };
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
console.log(a);
console.log(b);
console.log(c);

第36题

var a = 100;

function testResult() {
    var b = 2 * a;
    var a = 200;
    var c = a / 2;
    console.log(b);
    console.log(c);
}
testResult();

第37题

var tt = "aa";

function test() {
    console.log(tt);
    var tt = "dd";
    console.log(tt);
}
test();

第38题

var test = (function (a) {
    this.a = a;
    return function (b) {
        return this.a + b;
    }
}(function (a, b) {
    return a;
}(1, 2)));
console.log(test(1));

第39题

(function () {
    var a = b = 3;
})();
console.log("a defined?" + (typeof a !== "undefined"));
console.log("b defined?" + (typeof b !== "undefined"));

第40题

var myObject = {
    foo: "bar",
    func: function () {
        var self = this;
        console.log("outer func: this.foo=" + this.foo);
        console.log("outer func: self.foo=" + self.foo);
        (function () {
            console.log("inner func: this.foo=" + this.foo);
            console.log("inner func: self.foo=" + self.foo);
        }());
    }
};
myObject.func();

第41题

(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2)
    }, 100);
    setTimeout(function () {
        console.log(3)
    }, 0);
    console.log(4);
})();

第42题

var len = 4;
while (len--) {
    setTimeout(function () {
        console.log(len)
    }, 300);
    console.log(len);
}

第43题

window.name = "Window";
var cat = {
    name: "Cat"
};
var dog = {
    name: "Dog",
    sound: function (word) {
        console.log(this.name + word);
    }
};
dog.sound("is pooping");
dog.sound.call(window, "is banking");
dog.sound.call(dog, "is banking");
dog.sound.apply(cat, ["miaowing"]);

第44题

for (var i = 0, j = 0; i < 10, j < 6; i++, j++) {
    value = i + j;
}
console.log(value);

第45题

if (!("a" in window)) {
    var a = 1;
}
console.log(a);

第46题

var handle = function (a) {
    var b = 3;
    var temp = function (a) {
        b = a + b;
        return temp;
    }
    temp.toString = function () {
        return b;
    }
    return temp;
}
console.log(handle(4)(5)(6));

第47题

var res = ["1", "2", "3"].map(parseInt);
console.log(res);

第48题

var y = 1;  
if (function f(){}) {
    y += typeof f;  
}  
console.log(y);

第49题

var output = (function(x){
    delete x;
    return x;  
})(0);    
console.log(output);

第50题

var x = 1;
var output = (function(){
    delete x;
    return x;  
})();    
console.log(output);

第51题

var x = { foo : 1};
var output = (function(){
    delete x.foo;
    return x.foo;  
})();    
console.log(output);

第52题

var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

第53题

var trees = ["xyz","xxxx","test","ryan","apple"];
delete trees[3];    
console.log(trees.length);

第54题

var bar = true;
console.log(bar + 0);   
console.log(bar + "xyz");   
console.log(bar + true);   
console.log(bar + false);  

第55题

var z = 1, 
y = z = typeof y;
console.log(y);  

第56题

var foo = function bar(){
  return 12; 
};
typeof bar();  

第57题

var salary = "1000$"; 
 (function () {
     console.log("Original salary was " + salary);          var salary = "5000$";
     console.log("My New Salary " + salary); 
})();

第58题

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}
}
b();
console.log(a);

第59题

function foo(){
    function bar() {
        return 3;
    }
    return bar();
    function bar() {
        return 8;
    }
}
alert(foo());

第60题

function parent() {
    var hoisted = "I'm a variable";
    function hoisted() {
        return "I'm a function";
    }
    return hoisted(); 
}
console.log(parent());

第61题

alert(foo());
function foo() {
  var bar = function() {
    return 3;
  };
  return bar();
  var bar = function() {
    return 8;
  };
}

第62题

var myVar = 'foo';
(function() {
  console.log('Original value was: ' + myVar);
  var myVar = 'bar';
  console.log('New value is: ' + myVar);
})();

什么是作用域链?

标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)。

说一下数组都有哪些方法

concat()

把元素衔接到数组中。

every()

测试断言函数是否对每个数组元素都为真。

filter()

返回满足断言函数的数组元素。

forEach()

为数组的每一个元素调用指定函数。

🆕 find()

用于找出第一个符合条件的数组成员。如果所有成员都不符合条件,则返回-1。

🆕 findIndex()

返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

indexOf()

在数组中查找匹配元素。

join()

将数组的所有元素转化为字符串,并衔接起来。

lastIndexOf()

在数组中反向查找。

map()

从数组的元素中,计算出新的数组元素。

pop()

移除数组最后一个元素。

push()

把元素添加到数组尾部。

reduce()

从数组的元素中,计算出一个值。

reduceRight()

从右到左缩减数组。

reverse()

在原数组中颠倒数组元素的顺序。

shift()

移除数组的第一个元素。

slice()

返回数组的一部分。

some()

测试是否至少有一个数组元素能让断言函数为真。

sort()

在原数组中对数组元素进行排序。

splice()

插入、删除或替换数组元素。

toLocaleString()

将数组转化为本地化字符串。

toString()

将数组转化为字符串。

unshift()

在数组头部插入元素。

🆕 copyWithin()

将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。

🆕 fill()

fill方法使用给定值,填充一个数组。

🆕 includes()

表示某个数组是否包含给定的值,与字符串的includes方法类似。

ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象

静态方法

🆕 Array.from()

Array.from方法用于将两类对象转为真正的数组:类似数组的对象和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

🆕 Array.of()
Array.of方法用于将一组值,转换为数组。

理解对象

对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)它的属性。

属性包括名字和值。属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。值可以是任意JavaScript值,或者(在ECMAScript 5中)可以是一个getter或setter函数,除了名字和值之外,每个属性还有一些与之相关的值,称为“属性特性”

对象和属性

  • 内置对象是由ECMAScript规范定义的对象或类。例如,数组、函数、日期和正则表达式都是内置对象。
  • 宿主对象是由JavaScript解释器所嵌入的宿主环境(比如Web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLElement对象均是宿主对象。既然宿主环境定义的方法可以当成普通的JavaScript函数对象,那么宿主对象也可以当成内置对象。
  • 自定义对象是由运行中的JavaScript代码创建的对象。
  • 自有属性是直接在对象中定义的属性。
  • 继承属性是在对象的原型对象中定义的属性。

创建对象

  • 对象直接量
  • 通过new创建对象
  • 原型
  • Object.create()

elete运算符可以删除对象的属性。delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)。

JavaScript对象可以看做属性的集合,我们经常会检测集合中成员的所属关系——判断某个属性是否存在于某个对象中。可以通过in运算符、hasOwnPreperty()和propertyIsEnumerable()方法来完成这个工作,甚至仅通过属性查询也可以做到这一点。

  • in运算符的左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性则返回true
  • 对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false
  • propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性的可枚举性为true时它才返回true。

ECMAScript 5定义了两个用以枚举属性名称的函数。第一个是Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。

ECMAScript 5中第二个枚举属性的函数是Object.getOwnPropertyNames(),它和Ojbect.keys()类似,只是它返回对象的所有自有属性的名称,而不仅仅是可枚举的属性。

我们知道,对象属性是由名字、值和一组特性构成的。在ECMAScript5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称做“存取器属性”,它不同于“数据属性”,数据属性只有一个简单的值。当程序查询存取器属性的值时,JavaScript调用getter方法(无参数)。这个方法的返回值就是属性存取表达式的值。当程序设置一个存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter。(Vue的实现原理)

通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符。

要想设置属性的特性,或者想让新建属性具有某种特性,则需要调用Object.definePeoperty(),传入要修改的对象、要创建或修改的属性的名称以及属性描述符对象

面试题

1. call,apply,bind
2. 将类数组变成数组使用call例子
3. 求数组ary的最大值至少2中方法
4. 检测数据类型的四种方式
5. 函数的三种身份以及他的name属性和length属性的特点
6. 正则中的量词元字符有哪些分别是什么意思
7. 匹配首尾空格的正则
8. 解决贪婪性和懒惰性的方法
9. 正则exec捕获和字符串方法match方法的区别
10. 解析URL的方法

匀速运动动画

function linear(t, b, c, d) {
        return c / d * t + b
    }

    function tween(element, target, duration, callback) {
        let change = {};
        let begin = {};
        for (let key in target) {
            begin[key] = getCss(element, key);
            change[key] = removeUnit(target[key]) - begin[key];
        }

        let time = 0;
        let timing = setInterval(() => {
            time += 20;
            if (time >= duration) {
                clearInterval(timing);
                for (let key in target) {
                    setCss(element, key, target[key]);
                }
                callback && callback.call(element);
                return;
            }
            for (let key in target) {
                let current = linear(time, begin[key], change[key], duration);
                setCss(element, key, current);
            }
        }, 20)
    }

    function getCss(ele, attr) {
        let value = window.getComputedStyle(ele)[attr];
        return removeUnit(value);
    }

    function removeUnit(value) {
        let reg = /^[-+]?([1-9]\d+|\d)(\.\d+)?(px|pt|em|rem)$/;
        if (isNaN(value) && reg.test(value)) return parseFloat(value);
        if (isNaN(value)) return Number(value);
        return value
    }

    function setCss(ele, attr, val) {
        let reg = /^(width|height|top|bottom|left|right|(margin|padding)(Top|Left|Bottom|Right)?)$/;
        if (!isNaN(val) && reg.test(attr)) {
            ele.style[attr] = val + "px";
            return;
        }
        ele.style[attr] = val;
    }


    let box = document.getElementById("box");
    tween(box, {left: 500, top: 500}, 2000,function () {
        this.style.background = "pink";
    })

数据库查询语句

增加

INSERT INTO 表 (字段列表) VALUES (值列表)  

查询

SELECT 什么 FROM 表

6个经典的JavaScript报错分析

代码报错是经常发生的一件事,我们要确定是什么原因造成的,以及如何避免错误。

1. Uncaught TypeError: Cannot read property

类型错误

该错误说明没有某个属性,一般是该属性前面的值是undefined或者是null的情况会出现。

2. TypeError: ‘undefined’ is not a function

类型错误

执行对象中某个不存在的方法通常会报这种错误。说明在这个对象中没有该方法。

3. Uncaught RangeError: Maximum call stack

范围错误

递归的时候如果没有写边界条件判断就会报这种错误,因为函数一直在重复的执行。

4. TypeError: Cannot read property ‘length’

类型错误

报这种错误说明没有length属性,前面是null或者undefined的情况会报这种错误。

5. Uncaught TypeError: Cannot set property

类型错误

不能设置属性说明前面是null或者undefined的情况会报这种错误。

6. ReferenceError: event is not defined

image

报undefined错误大多情况是你没有声明该变量就直接使用。

总结:代码报错多是因为null和undefined造成的,所以在写代码的时候要考虑到一些特殊情况的处理,避免发生错误。

进制转换的方法

  1. 数字转换为字符串使用toString()方法的时候支持传入参数表示转换为多少进制
console.log(10..toString(16))// 数字10转换为16进制字符串是"a"
  1. 字符串转换为数字使用parseInt()方法的时候支持第二个参数表示以多少进制的解析
console.log(parseInt("a", 16))//  字符"a"解析为16进制的数字是10

变量提升必须注意的问题

声明变量

在使用一个变量的时候,我们必须先声明一个变量,我们共有 varletconst 这三个关键字可选择,具体使用哪个关键字,那就要根据业务场景来使用。声明一个变量可以赋值为任何数据类型的,因为JS的松散性,然后要注意的是声明变量的时候可以简写。

let a = 10, b = 20;

变量提升

我们在使用变量或函数的时候,理解什么时候被初始化值的是至关重要。变量提升是指在声明一个变量之前就使用了变量,在全局作用域中,只有使用var关键字声明的变量才会变量提升,变量提升的时候浏览器只知道有这么一个变量,但你下面定义的值还没有赋值给这个变量,这时候变量的值是undefined的,等到浏览器执行到下面的代码的时候才是一个赋值的过程。所以变量提升的时候没有初始化值。用var声明变量的时候回给window增加一个相同变量名的属性,所以你也可以通过属性名的方式获取这个变量的值,当没有使用任何关键字声明时,只是给一个变量赋值时,变量也相当于给window增加一个相同变量名的属性。

console.log(a); // -> undefined
var a = 10;
console.log(a); // -> 10
console.log(window.a); // -> 10

b = 10;
console.log(window.b); // -> 10

函数提升

定义一个函数可以使用函数声明和函数表达式,这两种方式在提升的时候也是有区别的,函数声明会提升到作用域的顶部,在提升的时候会分配一个内存空间,变量指向这个函数的内存空间,所以在定义一个函数之前是可以执行这个函数的,函数声明的方式定义函数会提升。而函数表达式就跟变量提升,仅仅只是声明,并没有给其赋值。

fn1() // -> "函数声明"
function fn1() {
    console.log("函数声明")
}

fn2() // -> "fn2 is not a function"
var fn2 = function () {
    console.log("函数表达式")
}

重复声明

var可以重复声明同一个变量,如果重复声明了,就相当于给变量赋值一样,用letconst在同一个作用域中是不可以重复声明同一个变量的,如果重复声明了会报错。关于函数表达式,如果重复的定义相同函数名的函数,后面的会覆盖前面的函数,因为JavaScript没有重载。

var a = 10;
var a = 100;
console.log(a); // -> 100

function fn() {
    return "第一个fn函数"
}
function fn() {
    return "第二个fn函数"
}
console.log(fn()) // -> "第二个fn函数"

特殊情况

1、在块级作用域中(如iffor),var声明变量function定义函数的时候,他们都是只声明但还未初始化值。2、return语句下面的语句也是会提升的,变量是只提前声明,函数是声明跟初始化值一起完成。3、return本身这条语句不会提升。4、自执行函数不会提升。5、回调函数不会提升。

综合练习

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}
}
b();
console.log(a);

答案:1

function foo(){
    function bar() {
        return 3;
    }
    return bar();
    function bar() {
        return 8;
    }
}
alert(foo());

答案:8

function parent() {
    var hoisted = "I'm a variable";
    function hoisted() {
        return "I'm a function";
    }
    return hoisted(); 
}
console.log(parent());

答案:“TypeError: hoisted is not a function”

alert(foo());
function foo() {
  var bar = function() {
    return 3;
  };
  return bar();
  var bar = function() {
    return 8;
  };
}

答案:3

var myVar = 'foo';
(function() {
  console.log('Original value was: ' + myVar);
  var myVar = 'bar';
  console.log('New value is: ' + myVar);
})();

答案:“Original value was: undefined”, “New value is: bar”

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.