JavaScript

 
JavaScript是一种脚本语言,像其他语言一样,可以做很多事,比如,后台开发,

但若说应用最普遍的,那还是前端开发:嵌入HTML,通过操作DOM动态修改页面。

并且这是跨平台的,这像句废话,因为前端的东西,各个跨平台,哪个组件不跨平台才需要提一下
但JavaScript又稍加特殊,它还是一门语言,这是将一门语言作为了页面的插件来使用并且普及天下... 

可以作为浏览器插件的语言还有Java,Rust,值得一提这三个语言背后的关系:

Mozilla基金会(火狐浏览器的研发组织),Sun(Java前研发公司),Oracle

1993年,马克·安德森领导的团开发了一个真正有影响力的浏览器Mosaic,这就是后来世界上最流行的浏览器Netscape Navigator。

1995年,微软推出了闻名于世的浏览器Internet Explorer。
       网景公司(Netscape)与Sun Microsystems合作,创建了互动性的Web站点。
       著名的 Brendan Eich 只花了 10 天时间就创建了第一个版本的 JavaScript 语言,
       这是一门动态编程语言,其语法上与 Sun 的 Java 语言大致类似。
       由于这种合作关系,Sun 公司因此持有了“JavaScript”的商标。


1998年,Netscape公司开放Netscape Navigator源代码,成立了Mozilla基金会。

2001年,最初浏览器写超过20行的javascript代码就会崩溃,直到2001年微软发布了ie6,首次实现对js引擎的优化和分离。
      (瞬间js提升到万行以上)

2003年,苹果公司发布了Safari浏览器。

2004年,Netscape公司发布了著名的开源浏览器Mozilla Firefox(中文俗称“火狐”)

2009 年,Oracle 收购了 Sun Microsystems,并因此获得了JavaScript的商标。

Rust 
Rust语言在2006年作为 Mozilla 员工 Graydon Hoare 的私人项目出现,
而 Mozilla 于 2009 年开始赞助这个项目。
第一个有版本号的 Rust 编译器于2012 年 1 月发布。Rust 1.0 是第一个稳定版本,于 2015年5月15日发布。

可以说 Mozilla基金会推出了JS,之后它推出的Rust其中一大特点就是可以作为插件运行在浏览器上,
弥补了JS作为解释性语言性能低的缺点,又能形成沙箱/黑盒,保护代码... 

这几十年对于技术发展来说,真是 ...风起云涌遥望峰之巅... 

ES 的特性

 
JS的语法比较灵活,或者说是松散,大规模开发会出现N多糟糕的局面,好几个公司都研发了自己的分支/版本,
微软推出了 JScript,  CEnvi推出 ScriptEase, 与JavaScript 同样可以在浏览器上运行,
Mozilla基金会是非赢利机构,你拿人家的东西修修改改就成自己,于双方 于社会发展 都不好 ... 
为了统一,或者说为了解决这个局面... 
欧洲计算机制造商联合会(European Computer Manufacturers Association,ECMA)提出了软件开发环境的参考模型:
ECMA 标准
之后,JavaScript 兼容于 ECMA 标准,也称为 ECMAScript 或者 ES

解释性语言 -- (不需要编译成文件)   跨平台

单线程(js引擎单线程)

嗯,后续写JS要遵守ES规范

 
<body>
    <script type="text/javascript">
        alert("11");
    </script>
</body>

 
<head>
    <title>Page Title</title>
    <script type="text/javascript" src="main.js"></script>
</head>

循环控制

 
true && false
true || false

if (true) {
    // 如果为真,则执行这里的代码
}

for (let i = 0; i < 5; i++) {
    console.log(i); // 把值输出到控制台
}

var fun = function(a) {
    return a;
};

 
typeof 37;              // 返回"number"
var myName = "aa";
typeof myName;          // 返回"string"
myName = true;
typeof myName;          // 返回"boolean"

11=='11'   //true 
11==='11'  //false 

变量作用域提升

 
JS可以将多个文件拼到一个文件中,那文件2中的变量拼接到新文件后,它的作用域会扩展到新文件吗?

会的 

如果不想遇到一些奇怪的问题最好使用ES规范

ES6 新增了let命令,用来声明变量。
它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效,也不允许重复声明
let声明的变量不会出现作用域提升的现象,更适合人的思维

function 原型Prototype

 
JS函数function是一个Function类型的对象,该对象有一个属性叫Prototype,即原型

可以通过原型为function添加一个属性/方法,就相当于高级语言中为类添加一个属性/方法 
没错,JS中提到原型,实际上就是将定义的function对象当类用了,原型定义类的属性/方法,供所有对象使用

而单个对象也可以重新定义属性/方法,这将覆盖原型中的定义 

将function当作类使用时,必须使用关键字:new

 
let TPF = function (){};
TPF.prototype.name = "自定义类";
TPF.prototype.log = function(msg){
    console.log(msg);
}

let ta = new TPF();
ta.log("test")

ES6字符串的新增方法

 
String.fromCodePoint()
String.raw()
实例方法:codePointAt()
实例方法:normalize()
实例方法:includes(), startsWith(), endsWith()
实例方法:repeat()
实例方法:padStart(),padEnd()
实例方法:trimStart(),trimEnd()
实例方法:matchAll()
实例方法:replaceAll()
实例方法:at()

ES6 允许为函数的参数设置默认值

 
function log(x, y = 'World') {
    console.log(x, y);
}

function Point(x = 0, y = 0) {
    this.x = x;
    this.y = y;
}

const p = new Point();
p // { x: 0, y: 0 }

参数默认值可以与解构赋值的默认值,结合起来使用

 
function foo({x, y = 5}) {
    console.log(x, y);
}

foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined

ES5属性简写:当属性名与值的变量名一致时,可以简化为一个

 
let name = "红云";
let person = {"name":name};
console.log(person);//Object { name: "红云" }

let person2 = {name};
console.log(person2);//Object { name: "红云" }

{name} 等价于 {"name":name}

 
function f(x, y) {
    return {x, y};
}

// 等同于

function f(x, y) {
    return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}
      

方法的简写

 
const o = {
    method() {
        return "Hello!";
    }
};

// 等同于

const o = {
    method: function() {
        return "Hello!";
    }
};
      

JS Map

 
let TPF = function (){};
TPF.prototype.name = "自定义类";
TPF.prototype.log = function(msg){
    console.log(msg);
}

let ta = new TPF();
ta.log("test")

ta.map_show = function(){
    let map = new Map();
    map.set('a', "A");
    map.set('b', false);
    map.set('c', 3);
    map.set('a', 4);//旧值被覆盖

    ta.log(map.size);//3
    ta.log(map.has('a'));//true
    map.clear();

    map.set('a', "A");
    map.set('b', false);
    map.set('c', 3);

    for (let key of map.keys()) {

    }


    for (let value of map.values()) {

    }

    //下面这三个for循环效果完全一样,选一个即可 
    for (let [key, value] of map) {
        console.log(key, value);
    }

    for (let item of map.entries()) {
        console.log(item[0], item[1]);
    }

    for (let [key, value] of map.entries()) {
        console.log(key, value);
    }

}
ta.map_show()

二维数组与map

 
const map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
  ]);
  
  [...map.keys()]
  // [1, 2, 3]
  
  [...map.values()]
  // ['one', 'two', 'three']
  
  [...map.entries()]
  // [[1,'one'], [2, 'two'], [3, 'three']]
  
  [...map]
  // [[1,'one'], [2, 'two'], [3, 'three']]

结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有map和filter方法)

 
const map0 = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c');
  
  const map1 = new Map(
    [...map0].filter(([k, v]) => k < 3)
  );
  // 产生 Map 结构 {1 => 'a', 2 => 'b'}
  
  const map2 = new Map(
    [...map0].map(([k, v]) => [k * 2, '_' + v])
      );
  // 产生 Map 结构 {2 => '_a', 4 => '_b', 6 => '_c'}
  

Proxy/拦截器

 
var obj = {name:3};
var p = new Proxy(obj, {
    get: function(target, propKey,receiver) {
        console.log(target);//Object { name: 3 }
        console.log(propKey);//age
        console.log(receiver);//更像是自身...
        return 37;
    },
    set: function (target, propKey, value, receiver) {
        console.log(`setting ${propKey}!`);
        return Reflect.set(target, propKey, value, receiver);
    },
    apply: function(target, thisBinding, args) {
        return args[0];
    },

    construct: function(target, args) {
        return {value: args[1]};
    }
});

console.log(p.age);//37 

 
var obj = {name:3};
var p = new Proxy(obj, {
    get: function(target, propKey,receiver) {
        if (propKey in target) {
            return target[propKey];
        } else {
            throw new ReferenceError(" \"" + propKey + "\" does not exist.");
        }
    }
});

console.log(p.name);//3
console.log(p.age);//Uncaught ReferenceError:  "age" does not exist.

JS let const

 
let 替代了var,让JS的变量与高级语言的变量靠近, 这是JS的一个巨大进步... 

const 与C的const一样,变量只读,需要在定义时就初始化

JS 数组

push 与 pop :可以直接将数组当栈使用

 
var a = [];   //创建一个空数组
var a = new Array("a","b","c");;   // 创建一个包含1项数据为20的数组
var a = ["a",2];   
let arr = ["a",2];   //js数组可以存放不同类型的数据,即使let也是如此

arr.push(3,4);
let tmp = arr.pop();
console.log(tmp);//4

arr.sort(compare)

 
function compare(value1, value2) {
    if (value1 < value2) {
    return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

arr = [3,1,2];   

arr.sort(compare);
console.log(arr); // [ 1, 2, 3 ]

arr.forEach

 
//1,2,3中index=0的元素为1 
//1,2,3中index=1的元素为2 
//1,2,3中index=2的元素为3
arr.forEach(function(x, index, a){
    console.log(a+"中index="+index+"的元素为"+x);
});

arr.map:带返回值

 
let ar2 = arr.map(function(item){
    return item*2;
});

console.log(ar2);  // [ 2, 4, 6 ]

//其中参数的个数,可以传1个也可以传多少
let arr = [3,1,2];   
let ar2 = arr.map(function(item,index){
    console.log(index);
    return item*2;
});

console.log(ar2);  // [ 2, 4, 6 ]

ar2.filter:带返回值,当符合条件时

 
ar2 = ar2.filter(function(x, index) {
    return index > 0;
});
console.log(ar2); //[ 4, 6 ]

... 解构数组

 


// ES6 的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);



复制数组
const a1 = [1, 2];
const a2 = a1;//引用

a2[0] = 2;
a1 // [2, 2]

const a1 = [1, 2];
// 写法一
const a2 = [...a1];//浅复制
// 写法二
const [...a2] = a1;
上面的两种写法,a2都是a1的克隆

引用实际的内容只有一个
浅复制,如果是简单类型,相当于独立的两份,如果是复合对象{}那还是相当于一份

合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

JS 箭头函数与this

 
BaGua.prototype.Pos = function(arr=[0,0,0],x,y){
    let dataset =[];
    // let h = this.height;
    //这里箭头函数使用外部的this 
    arr.forEach((d,i)=>{
        let xy = BaGua.prototype.getXy(d, x, y+this.height);
        dataset.push(...xy);
        // xy.forEach(function(v){
        //     dataset.push(v);
        // });
    });
    return dataset;

};


 
this.height的this指外部function(arr=[0,0,0],x,y)的this 
由于使用了prototype,外部function(arr=[0,0,0],x,y)的this 指的是 BaGua的this 

若是function则this.height将会找不到,此时的this指该function对象 
arr.forEach(function(d,i){
    let xy = BaGua.prototype.getXy(d, x, y+this.height);
    dataset.push(...xy);
    // xy.forEach(function(v){
    //     dataset.push(v);
    // });
});
JS apply and call

apply

 
对象2(对象2的参数...)
对象1 

对象2.apply(对象1,对象2的参数列表) = 对象2(对象2的参数...)

与 对象2(对象2的参数...)的不同之处在于:
- 对象2的this将会指向 对象1
- 如果对象2中压根就没使用this,那么传递对象1就没啥作用

 
var object2 = {
    msg: function() {
        return this.name + " " + this.age;
    },
    print: function(msg1,msg2){
        console.log(msg1+":"+msg2);
    }
}
var object1 = {
    name: "aaa",
    age: 33,
}
let m = object2.msg.apply(object1); 
console.log(m);//aaa 33 

//将object2的print打印功能给object1用一下
object2.print.apply(object1,["time","10"]);//time:10

call

 
对象2.call(对象1,对象2的参数1,对象2的参数2,...) = 对象2(对象2的参数...)

仅传参的形式不同,其他没啥差异
若对象2没参数,那就没啥区别

 
var object2 = {
    msg: function() {
        return this.name + " " + this.age;
    },
    print: function(msg1,msg2){
        console.log(msg1+":"+msg2);
    }
}
var object1 = {
    name: "aaa",
    age: 33,
}
let m = object2.msg.call(object1);  // 
console.log(m);//aaa 33 

//将object2的print打印功能给object1用一下
object2.print.call(object1,"time","10");//time:10

当不涉及this时,就是纯粹调用object2的上方法,可用null替换

 
object2.print.call(null,"time","10");//time:10
object2.print.apply(null,["time","10"]);//time:10

参考文章
Js apply()使用详解 
js apply()用法详解