SOURCE

/* 
* 类型检测
**
var arr = []
console.log(typeof arr )

var x = typeof x
var res = typeof typeof x
console.log(x, res) // undefined string

console.log([typeof null, null instanceof Object])


console.log(typeof typeof 0)

var arr = []
console.log(typeof arr, Object.prototype.toString.call(arr))

console.log(Object.prototype.toString.call(undefined))
*/


/*
* 类型转换
** 
console.log("2" + 3 + 4)

console.log('5' + 3, 5 + '3')

var a = parseInt('111办公室')
console.log(a)

var newArr = ['0x1', '0x2', '0x3'].map(val => parseInt(val))
console.log(newArr)

console.log(1 + '1')

console.log(parseInt('77', 40)) 
console.log(parseInt('77', 10)) 
console.log(parseInt('77', 2)) 
console.log(parseInt('77', 8)) 
console.log(parseInt('77', 16)) 
console.log(parseInt('77', 5)) 
*/

/*
* 逻辑判断
** 
console.log([5<6<3, 3<2<4])

console.log((2<3) || (3 < 2))

// 逻辑或和逻辑与 都是短路逻辑,如果左侧表达式为 true,则直接短路返回结果,不再运算右侧表达式
console.log(true || false && false, true && false || true)
*/


/**
 * 其他
 **
console.log(1 + - + + + - + 1)

console.log(['a',,'b',,].length)
*/

/*
* js 基础
** 
function showCase(value) {
    switch(value) {
    case 'A':
        console.log('Case A');
        break;
    case 'B':
        console.log('Case B');
        break;
    case undefined:
        console.log('Case undefined');
        break;
    default:
        console.log('Case default');
    }
}
showCase(new String('A')); // -> 'Case default'
showCase(String('A')); // -> 'Case A'



function funcA(x){
    var temp = 4;

    function funcB(y){
        console.log( ++x + y + (temp--));
    }

    funcB(5);
}
funcA(6)



var varArr = function(i,j,str) {
    console.log('j val is ', j)  
    return j == 0 ? str : varArr(i,--j,(str+= " " + i[j]));
}
var arr = new Array('apple','orange','peach','lime');
var str = varArr(arr,arr.length,"");
console.log(str);
// var i = ['apple', 'orange', 'peach', 'lime'], j = 4, // -> j = 3 str = 'lime'
// i = ['apple', 'orange', 'peach', 'lime'], j = 3, // -> j = 2 str = 'lime peach'
// i = ['apple', 'orange', 'peach', 'lime'], j = 2, // -> j = 1 str = 'lime peach orange'
// i = ['apple', 'orange', 'peach', 'lime'], j = 1, // -> j = 0 str = 'lime peach orange apple'
// i = ['apple', 'orange', 'peach', 'lime'], j = 0, str = 'lime peach orange apple'


function greetingMaker(greeting) { 
    function addName(name) {    
        greeting  = greeting.split(' ').reverse().join("-");
        return greeting + " " + name;
    }
    
    return addName;
}

var daytimeGreeting = greetingMaker("Good Day to you");
console.log(daytimeGreeting('shasha')); 



String.prototype.GetNum = function() { 
    var regEx = /[^\d]/g; 
    return this.replace(regEx, ''); 
};
var str = "a1b2c3";
str = str.GetNum();
console.log(str);



function sum(a, b) {
  return a + b;
}
var res = sum(1, "2");
console.log(res)



var str = "我非常喜欢编程";
str.length = 3;
console.log(str, str.length); // -> '我非常喜欢编程' 7

var arr = ['apple', 'orange', 'peach', 'lime']
arr.length = 2
console.log(arr) // ->  ['apple', 'orange']


var number = 0;
console.log(number++); // -> 0
console.log(++number); // -> 2
console.log(number); // -> 2

function nums(a, b) {
    if (a > b)
        console.log('a is bigger')
    else 
        console.log('b is bigger')
    return a + b
}
console.log(nums(4, 2))
console.log(nums(1, 2))


function side(arr) {
    arr[0] = arr[2];
}
function func1(a, b, c = 3) {
    c = 10;
    side(arguments);
    console.log('func1', arguments)
    console.log(a + b + c);
}
function func2(a, b, c) {
    c = 10;
    side(arguments);
    console.log('func2', arguments)
    console.log(a + b + c);
}
// 形参、实参 和 arguments 的关系: 
// 形参:声明函数时所有参数,称为形参。
// 实参:调用函数时传递的所有参数,称为实参
// arguments 是包含所有 实参 的类数组对象。arguments 对象的值 会自动同步 到对应的形参,当函数给默认值的时候,arguments 与形参的映射关系不存在
// 需注意的地方: 首先 arguments 对象的 长度由实参决定; 相对应的arguments 与 形参才会建立映射关系;
func1(1, 1, 1);
func2(1, 1, 1);



var a = 3;
var b = new Number(3);
var c = 3;
console.log(a == b);
console.log(a === b);
console.log(b === c);



var a = [];
a.push(1, 2);
a.shift(3, 4); // shift 删除数组的第一项,该方法不需要传递参数,有参数会忽略参数
a.concat([5, 6]); // concat 不会改变原数组,会返回一个新数组,需要变量接收
a.splice(0, 1, 2); // 将第0项删除,并替换为2
console.log(a)



var a = {}, b = '123', c = 123;
a[b] = 'b'; // ->  a['123'] = 'b'
a[c] = 'c'; // c变量的值作为对象的键名时,自动转换为string类型,此行执行后会相当于将上一行的重新赋值  a['123'] = 'c'
console.log(a, a[b]); // 'c'
// example 2
var a = {}, b = Symbol('123'), c = Symbol('123');
a[b] = 'b';
a[c] = 'c'; // b和c是两个不同的symbol变量值,所以a对象有两个属性
console.log(a, a[b]);
// example 3
var a = {}, b = {key:'123'}, c = {key:'456'};
a[b] = 'b'; // 对象类型的值作为对象的属性名时,调用对象的toString方法自动转换为string类型,值为"[object Object]",相当于a["[object Object]"] = 'b'
a[c] = 'c'; // 相当于a["[object Object]"] = 'c', 重置了上一行对a对象上"[object Object]"属性的值
console.log(a, a[b]);



console.log(null == undefined)
console.log(0.1 + 0.2 == 0.3) // 浮点数精确度的问题
console.log(typeof NaN) // -> 'number'
console.log(typeof Function)
console.log(typeof Object)
console.log(typeof {})
console.log('a' + 1) // -> 'a1'
console.log('a' - 1) // -> NaN
console.log(Function instanceof Object)
console.log(Object instanceof Function)


var array = []
for(var i = 0; i < 3; i++) {
    array.push(() => i)
}
var newArray = array.map(el => el())
console.log(newArray) // -> [3,3,3]


var a = function (m, n) {
    var b = function (l) {
        return l <= m ? l * b(l + 1) : 1;
    }

    return b(m - n + 1);
}

console.log(a(4, 2));
// m = 4, n = 2, l = 3
// 3 * b(4)
// b(4) = 4 * b(5)
// b(5) = 1
// 3 * 4 * 1 = 12


console.log(typeof undefined == typeof NULL, typeof NULL) // -> true  'undefined'
console.log(typeof function() {} == typeof class {}, typeof class {}) // -> true  'function'


var a = 10
var b = {
    age: 11
}
function fn(x,y) {
    --y.age;
    return --x;
}
// 函数之参数按值传递:参数如果是基本类型是按值传递,如果是引用类型按共享传递,而按共享传递是传递对象的引用的副本,但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了
// 按值传递:也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样
fn(a,b) 
console.log(a, b) // -> 10   { age: 10 }


var number = 4;
var numberFactorial = (function factorial (number){
    return (number === 0)? 1: number* factorial(number-1)
})(number)
console.log(numberFactorial)
// 4 * func(3)
// func(3) = 3 * func(2)
// func(2) = 2 * func(1)
// func(1) = 1 * func(0)
// func(0) = 1
// 4*3*2*1 = 24


// 逻辑或和逻辑与 都是短路逻辑,如果左侧表达式为 true,则直接短路返回结果,不再运算右侧表达式
const first = () => { console.log('first'); return false; }
const second = () => { console.log('second'); return true; }
console.log(first() && second() ); // -> 'first'  false   --> sencond 未执行
console.log( second() || first() ); // -> 'second'  true   -->  first 未执行

var arr=[1,2,3];
arr.push(arr.shift())
console.log(arr[1],arr[2])
*/


/* 
* this问题
**
var x = 1;

var obj = {
    x: 3,
    fun:function () {
        var x = 5;
        return this.x;
    }
};

var fun = obj.fun;
console.log(obj.fun(), fun()); // -> 3   1


var a = 5;
function test() { 
    a = 0; 
    console.log(a); 
    console.log(this.a, this); 
    var a;
    console.log(a); 
}
// new 运算符的实现机制:
// 1.创建一个新的空对象
// 2.设置原型,将对象的原型设置为函数的prototype对象
// 3.让函数的this指向这个对象,指向函数的代码(为这个新对象添加属性)
// 4.判断函数的返回值类型,如果是值类型,返回创建的对象;如果是引用类型,就返回这个引用类型的对象
new test(); // -> 0  undefined  {}  0



// 这道题考察的是箭头函数中没有this的这个特性
function fun () {
    return () => {
        return () => {
            return () => {
                console.log(this.name)
            }
        }
    }
}
var f = fun.call({name: 'foo'}) // 将fun函数里的this绑定为{name: 'foo'}, 
// fun() 执行返回的f函数是箭头函数, 箭头函数里没有this,不能通过call、apply、bind改变this执行,
// 箭头函数内的this需要向上层作用域查找, -> fun的作用域中的this
var t1 = f.call({name: 'bar'})()()
var t2 = f().call({name: 'baz'})()
var t3 = f()().call({name: 'qux'})



const Person = (name="wang",age=10) => {
   this.name = name;
   this.age = age;
   return this.name +' is '+ this.age + 'years old'
}
console.log(Person.prototype)

// tip: 箭头函数内没有this, new.target, 没有原型对象 不能使用new 关键字
// let result = new Person('zhang',11)
// console.log(result)



var name = 'global';
var obj = {
    name: 'local',
    foo: function(){
        this.name = 'foo';
    }.bind(window)
};
var bar = new obj.foo();
setTimeout(function() {
    console.log(window.name);
}, 0);
console.log(bar.name);
 
var bar3 = bar2 = bar;
bar2.name = 'foo2';
console.log(bar3.name);


var obj = {
    name:"zhangsan",
    sayName:function(){
        console.log(this.name);
    }
}

var wfunc = obj.sayName;
obj.sayName();
wfunc(); // -> '' Tips: name属性比较特殊 默认情况下 window.name = ''
var name = "lisi"; // window.name = 'lisi'
obj.sayName();
wfunc();
*/



/* 事件循环
**
const promiseA = Promise.resolve('a')
promiseA.then((res) => {
    console.log('promiseA1', res)
}).then((res) => {
    console.log('promiseA2', res)
})

const promiseB = Promise.resolve('b')
promiseB.then((res) => {
    console.log('promiseB1', res)
})
promiseB.then((res) => {
    console.log('promiseB2', res)
})
// -> promiseA1 promiseB1  promiseB2  promiseA2



setTimeout(() => {
    console.log(1)
}, 0)

const P = new Promise((resolve, reject) => {
    console.log(2)
    setTimeout(() => {
        resolve()
        console.log(3)
    }, 0)
})

P.then(() => {
    console.log(4)
})
console.log(5)



setTimeout(function(){
    console.log(1);
}, 0)
new Promise(function(resolve){
    console.log(2);
    resolve();
    console.log(3);
}).then(function(){
    console.log(4);
})
console.log(5);



(async () => {
    console.log(1);
    setTimeout(() => {
        console.log(2);
    }, 0);
    // await 后面的Promise没有执行resolve/reject回调函数,状态不会发生改变即一直是pending状态,所以then函数里不会执行,
    // 而且await会阻塞后面代码的执行,所以5也不会输出
    await new Promise((resolve, reject) => {
        console.log(3);
    }).then(() => {
        console.log(4);
    });
    console.log(5);
})();


new Promise((resolve) => {
    console.log('1')
    resolve()
    console.log('2')
}).then(() => {
    console.log('3')
})
setTimeout(() => {
    console.log('4')
})
console.log('5')


var p1 = new Promise(function(resolve, reject){
    resolve("2")
})
setTimeout(function(){
    console.log("1")
},10)
p1.then(function(value){
    console.log(value)
})
setTimeout(function(){
    console.log("3")
},0)



setTimeout(function() {
  console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
}).then(function() {
  console.log('promise2');
}).then(function() {
  console.log('promise2');
}).then(function() {
  console.log('promise2');
}).then(function() {
  console.log('promise2');
}).then(function() {
  console.log('promise2');
});


setTimeout(function() {
    console.log(1)
},0)
new Promise(function executor(resolve){
    console.log(2)
    for (var i = 0; i<10000; i++) {
        i - 9999 && resolve()
    }
    console.log(3)
}).then(function() {
    console.log(4)
})
console.log(5)
*/

/**
 * 原型和原型链


var Foo = (function() {
    var x = 0;
    function Foo() {}
        Foo.prototype.increment = function() {
        ++x;
        console.log(x);
    };
    return Foo;
})();
 
var a = new Foo();
a.increment(); // -> 1
a.increment();  // -> 2
var b = new Foo();
a.increment();  // -> 3


var name = 'Jay'
function Person(name){
    this.name = name;
    console.log(this.name)
}
var a = Person('Tom') // -> 'Tom'.  当普通函数调用,全局环境下this指向window,函数体内没有return语句则函数调用后返回值为undefined
console.log(name)  // -> 'Tom'
console.log(a) // -> undefined
var b = new Person('Michael') // -> 'Michael' 作为构造函数调用
console.log(b) // { name: 'Michael'}


// es6 -- class 类 和 类的继承
class A{}
class B extends A{}
const a = new A()
const b = new B()
console.log(a.__proto__ === A.prototype)
console.log(b.__proto__ === B.prototype)
console.log(B.__proto__ === A)
console.log(B.prototype.__proto__  === A.prototype)
console.log(b.__proto__.__proto__  === B.prototype.__proto__)



function test() {           
    getName = function() { 
        Promise.resolve().then(() => console.log(0)); 
        console.log(1);               
    };

    return this; 
}
test.getName = function() { 
     setTimeout(() => console.log(2), 0); 
     console.log(3);               
};
test.prototype.getName = function() {    
     console.log(4); 
};  
// 预解释阶段:变量提升和函数提升  执行阶段: 对getName变量赋值操作,覆盖了函数声明
var getName = function() { 
     console.log(5);             
};
function getName() {
     console.log(6); 
}      
      
test.getName(); 
getName(); 
test().getName(); // 执行test()时,函数体中的this是window,所以相当于重新为全局的getName函数赋值了, return this语句指定了test函数的返回值为window对象,所以可以直接链式调用getName方法
getName();  // 上一行对getName函数重新赋值了,调用最新赋值的函数体执行
new test.getName(); // 将test对象上的getName函数作为构造函数,用new运算符执行
new test().getName(); // new test() 返回一个空对象,执行test原型对象上的getName方法
new new test().getName(); // new test()返回一个空对象 读取test原型对象上的getName方法作为构造函数,用new运算符执行
// test.getName() -> 3  宏任务1 console.log(2)
// getName() -> 5
// test().getName() ->  1 微任务1 console.log(0)
// getName() -> 1  微任务2 console.log(0)
// new test.getName() // ->  3  宏任务2 console.log(2)
// new test().getName() // ->  4
// new new test().getName() // ->  4
// 3 5 1 1 3 1 1 4 4 0 0 2 2 




const Book = {
  price: 32
}
const book = Object.create(Book); // 以Book为原型对象创建book对象,Book上的属性和方法都在book的原型上了
book.type = 'Math'; // 添加私有属性
delete book.price; // 删除私有属性,不影响原型对象上的属性
delete book.type; // 删除私有属性
console.log(book.price); // ->  32
console.log(book.type); // ->  undefined

*/

/**
 * 作用域和预编译
**
function sayHello() {
    console.log(name); // -> undefined
    console.log(age); // Uncaught ReferenceError: Cannot access 'age' before initialization
    var name = "Tom";
    let age = 18;
} 
sayHello();



var foo = "Hello";
(function(){
    var bar = " World";
    console.log(foo + bar); // -> 'Hello World'
})();
console.log(foo + bar); // -> Uncaught ReferenceError: bar is not defined



var a = 10;
(function () {
    console.log(a) // -> undefined
    a = 5
    console.log(window.a) // -> 10
    var a = 20;
    console.log(a) // -> 20
})()



"use strict"
var name = 'Jay'
var person = {
    name: 'Wang',
    pro: {
        name: 'Michael',
        getName: function () {
            return this.name
        }
    }
}
console.log(person.pro.getName()) // -> 'Michael'
// 严格模式下 全局对象不再指向window,而是undefined
var people = person.pro.getName 
console.log(people()) // -> Uncaught TypeError: Cannot read properties of undefined (reading 'name')

*/

/*
**  ES6

// ES6  - 对象
const student = {name: 'ZhangSan'}
Object.defineProperty(student, 'age', {value: 22})
console.log(student)
console.log(Object.keys(student))



//  ES6 - generator
function * cb(x, y) {
    for(let i = Math.ceil(x); i <= y; i++) {
        yield i;
    }
}
var a = cb(6, 9);
console.log(a.next());
console.log(a.next());



//  ES6 - 运算符之rest运算符
function fn(...args) {
  console.log(typeof args);
}
fn(21); // -> 'object'


//  ES6 - promise
Promise.reject(0)
       .catch(e => e)
       .catch(e => console.log(e))



// ES6 - class
class Person {
     constructor (name) {
          this.name = name;
     }
     greet () {
          console.log(`Hi, my name is ${this.name}`);
     }
     greetDelay (time) {
          setTimeout(() => {
               console.log(`Hi, my name is ${this.name}`);
          }, time);
  
    }
}
// ES6 的 class 编译后所生成的ES5代码
function Person(name) {
    this.name = name
}

Person.prototype.greet = function() {
    console.log(`Hi, my name is ${this.name}`);
}

Person.prototype.greetDelay = function(time) {
    setTimeout(() => {
        console.log(`Hi, my name is ${this.name}`);
    }, time);
}

// es6 - 标签模板
function getPersonInfo (one, two, three) {
    console.log(one) // -> ["", " is ", " years old"]
    console.log(two) // -> 'Lydia'
    console.log(three) // -> 21
}
const person = 'Lydia'
const age = 21
getPersonInfo `${person} is ${age} years old`



// ES6 - module
// module.js
export default () => "Hello world"
export const name = "nowcoder"
export const person = {
    name: "nowcoder",
    age: 18
}
// index.js
import * as data from "./module"
console.log(data) // -> { default: () => "Hello world", name: "nowcoder", person: { name: "nowcoder",
    age: 18 } 
}
data.person.age = 24

// other.js
import * as data from "./module"
console.log(data.person) // { name: "nowcoder", age: 24 } 
*/


// url上获取查询参数
function getUrlParam(sUrl, sKey) {
    const startIndex = sUrl.indexOf('?') + 1
    const endIndex = sUrl.indexOf('#')
    let paramsStr = sUrl.slice(startIndex, endIndex)
    if (!paramsStr) return ''
    let params = paramsStr.split('&')
    let obj = {}
    params.forEach((item) => {
        const [key, val] = item.split('=')
        obj[key] = obj[key] ? obj[key].concat(val) : [val]
    })
    if (sKey) {
        return obj[sKey] || ''
    }  
    return obj
}

var res = getUrlParam('http:xxx?key=1&key=2&key=3&test1=4#hashkey', 'test1')
// console.log(res)


// 节流:滚动加载
function throttle(fn,delay) {
    var lastTime = 0;
    return function() {
        var nowTime = Date.now()
        if (nowTime - lastTime >= delay) {
            fn.apply(this, arguments)
            lastTime = nowTime
        }
    }
}


function createObject() {
    let constructor = Array.prototype.shift.call(arguments)
    if (typeof constructor !== 'function') {
        throw new Error('type error')
    }
    let newObj = Object.create(constructor.prototype)
    let result = constructor.apply(newObj, arguments)
    let flag = result && (typeof result === 'object' || typeof result === 'function')
    return flag ? result : newObj
}


function promiseAll(promises) {
    return new Promise(function(resolve, reject) {
        if (!Array.isArray(promises)) {
            throw new TypeError('argument must be a array')
        }
        let resolvedCounter = 0
        let promiseNum = promises.length
        let resolveResult = []
        for(let i = 0; i < promiseNum; i++) {
            Promise.resolve(promises[i]).then((res) => {
                resolvedCounter++
                resolveResult[i] = res
                if (resolvedCounter === promiseNum) {
                    return resolve(resolveResult)
                }
            }, error => {
                return reject(error)
            })
        }
    })
}

let p1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(1)
    }, 1000)
})
let p2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(2)
    }, 2000)
})
let p3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(3)
    }, 3000)
})

// promiseAll([p3, p1, p2]).then(res => {
//     console.log(res) // [3, 1, 2]
// })

Promise.race = function(promises) {
    return new Promise(function(resolve, reject) {
        for(let i = 0, len = promises.length; i < len; i++) {
            promises[i].then(resolve, reject)
        }
    })
}


let promise1 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       reject(1);
	},2000)
});
let promise2 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       resolve(2);
	},1000)
});
let promise3 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       resolve(3);
	},3000)
});

// Promise.race([promise1,promise2,promise3]).then(res=>{
// 	console.log('success >>>', res); // success >>>,2
// },rej=>{
//     console.log('error >>>>', rej)
// })

let promise4 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       reject(1);
	},1000)
});
let promise5 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       reject(2);
	},1000)
});
let promise6 = new Promise((resolve,reject)=>{
	setTimeout(()=>{
       reject(3);
	},3000)
});
// Promise.race([promise3,promise4,promise5]).then(res=>{
// 	console.log('success', res); 
// },rej=>{
//     console.log('error', rej)  // -> error, 1
// })


Promise.finally = function(onDone) {
    this.then(onDone, onDone)
}

Promise.resolve = function(value) {
    if (value && value instanceof Promise) {
        return value
    } else if (value && typeof value === 'object' && typeof value.then === 'function') {
        return new Promise(resolve => value.then(resolve))
    } else if (value) {
        return new Promise(resolve => resolve(value))
    } else {
        return new Promise(resolve => resolve())
    }
}

Promise.finally = function(cb) {
    return this.then(data => {
        return Promise.resolve(cb()).then(() => data)
    }, err => {
        return Promise.resolve(cb()).then(() => { throw err })
    })
}

// 防抖 - 避免用户的多次点击向后端发送多次请求
function debounce(fn, wait) {
    let timer = null
    return function() {
        let context = this, arg = arguments
        if (timer) {
            clearTimeout(timer)
            timer = null
        }
        timer = setTimeout(() => {
            fn.apply(context, arg)
        }, wait)

    }
}


// 节流 - onscroll事件,每隔一段时间触发一次,降低事件调用的次数
function throttle(fn, delay) {
    let lastTime = Date.now()

    return function() {
        let context = this, args = arguments, nowTime = Date.now()
        if (nowTime - lastTime >= delay) {
            lastTime = Date.now()
            return fn.apply(context, args)
        }
    }
}


// 类型判断函数
function getType(value) {
    if (value == null) {
        return value + ''
    }
    if (typeof value === 'object') {
        let valueClass = Object.prototype.toString.call(value),
        type = valueClass.split(" ")[1].slice(0, -1)
        return type.toLowerCase()
    } else {
        return typeof value
    }
}

// console.log(getType([]))

// 手写Object.create()
function create(obj) {
    function F() {}
    F.prototype = obj
    return new F()
}

// 手写instanceof 方法
function myInstanceof(left, right) {
    let proto = Object.getPrototypeOf(left),
        prototype = right.prototype

    while(true) {
        if (!proto) return false
        if (proto === prototype) return true

        proto = Object.getPrototypeOf(proto)
    }    
}

// 手写 new 操作符
function createObject(fn, ...arg) {
    if (typeof fn !== 'function') {
        throw new Error('fn is not a function')
    }
    var obj = Object.create({})
    obj.__proto__ = fn.prototype
    const result = fn.apply(obj, arg)
    return result && (typeof result === 'object' || typeof result === 'function') ? result : obj
}

let mapData = new Map([
    ['foo', 1],
    ['bar', 2]
])

// console.log(mapData.size)
// console.log(mapData.has('foo'))

mapData.set('baz', 3)
// console.log(mapData.get('bar'))

mapData.delete('foo')
// console.log(mapData.has('foo'))

mapData.clear()
// console.log(mapData.size)
// console.log(mapData.get('bar'))

var obj = { a: 1}
mapData.set(obj, 4)
// console.log(mapData.has(obj), mapData.get(obj))

var null222 = null
mapData.set(null222, 5)
// console.log(mapData.has(null222), mapData.get(null222))

let mapData1 = new Map([
    ['foo', 1],
    ['bar', 2]
])


for(let key of mapData1.keys()) {
//     console.log('mapData1 key', key)
}
for(let val of mapData1.values()) {
//     console.log('mapData1 value', val)
}

for(let [key, val] of mapData1.entries()) {
//     console.log('mapData1', key, val)
}

mapData1.forEach((val, key, map) => {
//     console.log('mapData1', val, key)
})

// let arr =  ['a', 'b', 'c']

// arr.forEach((item, index, a) => {
//     console.log('array forEach', item, index, a)
// })

// 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。
// 而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制。
let weakMapData = new WeakMap()

weakMapData.set(obj, 4)
// console.log(weakMapData.has(obj), weakMapData.get(obj))

// weakMapData.set('obj', 4)
// console.log(weakMapData.has('obj'), weakMapData.get('obj'))

// weakMapData.set(null222, 5)
// console.log(weakMapData.has(null222), weakMapData.get(null222))

function Person(name) {
    this.name = name
}
// 修改原型
Person.prototype.age = 18
var p = new Person('hello')
p.height = 180


for (let key in p) {
    if (p.hasOwnProperty(key)) {
        // 遍历实例自己的属性,不遍历原型上的属性方法
        // console.log(key) // -> name height
    }
}

var arr1 = [1,2,3]
Array.prototype.aa = 'aa'
arr1.bb = 4
for(let i in arr1) {
    // console.log(i) // ->  0  1 2 bb aa
}

for (let i of arr1) {
    // console.log(i) // -> 0  1 2 
}


Function.prototype.myCall = function(context) {
    if (typeof this !== 'function') {
        throw new Error('type error')
    }
    let arg = [...arguments].slice(1), result = null
    context = context || window
    context.fn = this
    result = context.fn(...arg)
    delete context.fn
    return result
}

Function.prototype.myApply= function(context) {
    if (typeof this !== 'function') {
        throw new Error('type error')
    }
    let arg = [...arguments].slice(1), result = null
    context = context || window
    context.fn = this
    result = context.fn(arg)
    delete context.fn
    return result
}

Function.prototype.myBind= function(context) {
    if (typeof this !== 'function') {
        throw new Error('type error')
    }
    let args = [...arguments].slice(1), fn = this
    return function Fn() {
        // 根据调用方式,传入不同绑定值
        return fn.apply(
            this instanceof Fn ? this : context, 
            args.concat([...arguments])
        )
    }
}


// 字符串出现的不重复最长长度
function lengthOfLongestSubstring(str) {
    let map = new Map(), i = -1, res = 0, n = str.length
    for(let j = 0; j < n; j++) {
        if (map.has(str[j])) {
            i = Math.max(i, map.get(str[j]))
        }
        res = Math.max(res, j - i)
        map.set(str[j], j)
    }
    return res
}

// console.log(lengthOfLongestSubstring('bwabfwb'))

// 将数字每千分位用逗号隔开
function formatNum(n) {
    let num = n.toString(),  
        intNum = num, 
        decimals = '', 
        index = num.indexOf('.')

    if (index > -1) {
        [intNum, decimals] = num.split('.')
        // decimals = num.split('.')[1]
        // intNum = num.slice(0, index)
    }
    if (intNum.length <= 3) return num

    let intNumArr = intNum.split('').reverse(),
        lastIndex = intNumArr.length - 1,
        tempArr = []

    intNumArr.forEach((item, index) => {
        tempArr.push(item)
        if (index > 0 && index % 3 === 2 && index < lastIndex) {
            tempArr.push(',')
        }
    })
    intNum = tempArr.reverse().join('')
    return decimals ? intNum + '.' + decimals : intNum
}

// console.log(formatNum(344412323.33)) // -> 344,412,323.33

// 交换a,b的值,不能用临时变量 
var a = 3, b = 2
// 巧妙的利用两个数的和、差:
a = a + b
b = a - b
a = a - b
// console.log(a, b)

// 实现数组的乱序输出 -> 方案1
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,
    randomIndex,
    temp;
while (length) {
    randomIndex = Math.floor(Math.random() * length--);
    [arr[length], arr[randomIndex]] = [arr[randomIndex], arr[length]];
}
// console.log(arr, arr.length)

// 实现数组的乱序输出 -> 方案2
var arr = [1,2,3,4,5,6,7,8,9,10], lastIndex = arr.length - 1;
for (var i = 0; i <= lastIndex; i++) {
  const randomIndex = Math.round(Math.random() * (lastIndex - i)) + i;
  [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
// console.log(arr, arr.length)

var arr = [1,2,3,4,5,6,7,8,9,10]
var sum = arr.reduce((sum, next) => sum += next, 0)
// console.log(sum) // -> 55

function add(arr) {
    if (arr.length == 1) return arr[0] 
    return arr[0] + add(arr.slice(1)) 
} 
// console.log(add(arr)) // -> 55

var arr = [1,2,3,[[4,5],6],7,8,9]
// console.log(arr.toString(), arr.toString().split(','))
var sum = arr.toString().split(',').reduce((total,i) => total += Number(i),0);
// console.log(sum); // -> 45

var arr = [1, [2, [3, [4, 5]]], 6];
function flatten(arr) {
  let str = JSON.stringify(arr);
  str = str.replace(/(\[|\])/g, '');
  str = '[' + str + ']';
  return JSON.parse(str); 
}
// console.log(flatten(arr));

function flatten(arr) {
  return arr.flat(Infinity);
}
// console.log(flatten(arr));

function flatten(arr) {
    return arr.toString().split(',');
}
// console.log(flatten(arr));

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}
// console.log(flatten(arr));

function flatten(arr) {
    return arr.reduce(function(prev, next){
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
}
// console.log(flatten(arr));

function flatten(arr) {
  let result = [];

  for(let i = 0; i < arr.length; i++) {
    if(Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
// console.log(flatten(arr));

function _flat(arr, depth) {
    if (!Array.isArray(arr) || depth <= 0) {
        return arr
    }
    return arr.reduce((prev, cur) => {
        if(Array.isArray(cur)) {
            return prev.concat(_flat(cur, depth - 1))
        } else {
            return prev.concat(cur)
        }
    }, [])
}

function add(m) {
    var temp = function(n) {
        return add(m + n)
    }
    temp.toString = function() {
        return m
    }
    return temp
}

// console.log(add(3)(4)(5))

// function _flat(arr, depth) {
//   if(!Array.isArray(arr) || depth <= 0) {
//     return arr;
//   }
//   return arr.reduce((prev, cur) => {
//     if (Array.isArray(cur)) {
//       return prev.concat(_flat(cur, depth - 1))
//     } else {
//       return prev.concat(cur);
//     }
//   }, []);
// }

// var arr = [1, [2,3], 4, [5, [6,7]]]
// console.log(_flat(arr, 2))

function _flat(arr, depth = 1){
    if(!Array.isArray(arr) || depth <= 0) {
      return arr
    }
    return arr.reduce((prev, cur) => {
        if (Array.isArray(cur)) {
        return prev.concat(_flat(cur, depth - 1))
        } else {
        return prev.concat(cur);
        }
    }, []);
}

// var arr = [1, [2,3], 4, [5, [6,7]]]
// console.log(_flat(arr, 2))



function test(num) {
    const chars = num.toString().split('')
    const len = chars.length
    let index = 0, res = 0

    while(index < len) {
        res += chars[index] * chars[index]
        index++
    }
    console.log(res)
    return res == 1 ? true : test(res)
}

// console.log(test(19))
console 命令行工具 X clear

                    
>
console