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.
let 替代了var,让JS的变量与高级语言的变量靠近, 这是JS的一个巨大进步... const 与C的const一样,变量只读,需要在定义时就初始化
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' ]
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); // }); });
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()用法详解