js学习笔记(一)
1.数组实用方法大全
1 //给数组添加个方法,返回数组中的最大值 2 Array.prototype.max = function() { 3 return Math.max.apply(null,this); 4 } 5 [1,2,3,4].max(); //4 6 7 //给数组添加个方法,给数组去重 8 Array.prototype.unique = function() { 9 return this.filter((item, index, arr) => arr.indexOf(item) === index); 10 } 11 [11,2,1,1,2,3,1,2,4,5,23,2].unique(); //[11, 2, 1, 3, 4, 5, 23]
1 var arr = []; 2 function distinct(arr){ 3 return arr.filter(function(ele,index,arr){ 4 return arr.indexof(ele,index+1)==-1 5 }) 6 } 7// 这个方法也可以用来去重
Array.isArray()
在ES5之前不支持,就自己写。不过现在都到ES6了,可以不管了。
1 Array.prototype.isArray = Array.prototype.isArray || function() { 2 return Object.prototype.toString.call(this) === "[object Array]"; 3 } 4 [1,2,3].isArray(); //true
数组的遍历
1 for (var index = 0; index < arr.length; index++) { 2 console.log(arr[index]); 3 } //这种写法简洁,但这种方法也有一个小缺陷:你不能使用break语句中断循环,也不能使用return语句返回到外层函数。 4 5 for (var value of arr) { 6 if(value == 2){break;} 7 console.log(value); //1 8 } //这是最简洁、最直接的遍历数组元素的语法。这个方法避开了for-in循环的所有缺陷。与forEach()不同的是,它可以正确响应break、continue和return语句。
splice
插入、删除、换数
1 var arr = ["q","w","e"]; 2 //删除 3 var removed = arr.splice(1,1); 4 console.log(arr); //q,e 已被改变 5 console.log(removed); //w ,返回删除的项 6 //插入 7 var insert = arr.splice(0,0,"r"); //从第0个位置开始插入 8 console.log(insert); //返回空数组 9 console.log(arr); //r,q,e 10 //替换 11 var replace = arr.splice(1,1,"t"); //删除一项,插入一项 12 console.log(arr); //r,t,e 13 console.log(replace); //q,返回删除的项
sort() 方法对数组的元素做原地的排序,并返回这个数组。
1 var arr = [1,2,4,3,1,1,2]; 2 console.log(arr.sort());//[1, 1, 1, 2, 2, 3, 4] 3 4 然而: 5 var arr = [1,2,10,4,3,1,1,2]; 6 console.log(arr.sort());//[1, 1, 1, 10, 2, 2, 3, 4]
这是因为sort
排序可能是不稳定的,默认按照字符串的Unicode码位点排序。
1 var arr = [1,2,10,4,3,1,1,2]; 2 console.log(arr.sort(function(a,b){ 3 return a-b; 4 })); // [1, 1, 1, 2, 2, 3, 4, 10] 5 //这个函数就是我们自己控制了,我们想要什么样的排序就改变这个参数函数的逻辑即可。
var aa= [1,4,3,2,8,7]; aa.sort(function(){return Math.random()-0.5;}) ; console.log(aa); //随意打乱数组
slice
截取、转化arguments伪数组
1 function test() { 2 var arr = arguments; 3 arr.push('xza'); 4 console.log(JSON.stringify(arr)); 5 } 6 test(1,2,3); //arr.push is not a function(…) 因为伪数组没有push方法 7 8 转换后: 9 function test() { 10 var arr = Array.prototype.slice.call(arguments); 11 arr.push('xza'); 12 console.log(JSON.stringify(arr)); 13 } 14 test(1,2,3); //[1,2,3,"xza"] 15 //slice方法经常用来截取一个数组,不过它更常用在将伪数组转化为真数组。平时我们的函数传的参数arguments是一个伪数组,很多数组的方法不能使用,我们就需要将伪数组转化为真数组。
1 var arr1 = [1,2,3]; 2 var arr2 = [4,5,6]; 3 var arr3 = arr1.concat(arr2); //[1, 2, 3, 4, 5, 6] 4 arr3.concat(7); //[1, 2, 3, 4, 5, 6, 7] 5 //我们平时都是这么使用的,如果需要连接两个数组的元素时,中间插元素,可以 6 7 var arr3 = arr1.concat('xzavier', arr2); //[1, 2, 3, "xzavier", 4, 5, 6]
其他方法
方法 使用
concat() 连接两个或更多的数组,并返回结果。
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
reverse() 颠倒数组中元素的顺序。
toString() 把数组转换为字符串,并返回结果。
toLocaleString() 把数组转换为本地数组,并返回结果。
valueOf() 返回数组对象的原始值
map() 返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。
every() 测试数组的所有元素是否都通过了指定函数的测试。
some() 测试数组中的某些元素是否通过了指定函数的测试。
1 ar arr = ['xzavier',123,'jser']; 2 console.log(arr.valueOf()); //['xzavier',123,'jser'] 3 console.log(arr.toString()); //xzavier,123,jser 4 console.log(arr.toLocaleString()); //xzavier,123,jser 5 var arr = ['xzavier',123,'jser']; 6 console.log(arr.join(',')); //xzavier,123,jser 7 var arr = [1,2,3]; 8 console.log(arr.reverse()); //[3,2,1] 9 var numbers = [1, 4, 9]; 10 var roots = numbers.map(Math.sqrt); //[1,2,3] 11 numbers //[1,4,9] 12 roots // [1,2,3] 13 [2, 5, 1, 4, 3].some(function (element, index, array) { 14 return (element >= 10); 15 }); //false 16 [2, 5, 1, 4, 13].some(function (element, index, array) { 17 return (element >= 10); 18 }); //true 19 [2, 5, 1, 4, 13].every(function (element, index, array) { 20 return (element >= 10); 21 }); //false 22 [2, 5, 1, 4, 13].every(function (element, index, array) { 23 return (element >= 0); 24 }); //true
思考题:有两个字符串s1和s2,值只能为a-z。现写一函数,返回一个新的升序的字符串,其值由s1、s2中的值组成,要求包含最多字符且不能重复。
1 a = "xyaabbbccccdefww" 2 b = "xxxxyyyyabklmopq" 3 longest(a, b) -> "abcdefklmopqwxy 4 5 //第一种方案 6 function longest(s1, s2) { 7 let distStr, 8 value, 9 distArr = [] 10 getDistinct(distArr, s1 + s2) 11 // 数组排序并转成字符串 12 distStr = distArr.sort().join('') 13 return distStr 14 } 15 // 数组去重 16 function getDistinct(target, source) { 17 let value 18 // 将字符串转成数组 19 source = Array.from(source) //es6方法 20 for(value of source) { 21 // 如果target数组中没有该value,则将其添加到数组中 22 if(!target.includes(value)) { 23 target.push(value) 24 } 25 } 26 }
用ES6中提供的Set
数据结构对字符串(s1+s2)“去重”,然后结构赋值得到数组,最后进行排序并转成字符串
const longest = (s1, s2) => [...new Set(s1+s2)].sort().join('')
2.表单操作实例
利用焦点事件(focus 事件),在文本框获得焦点时,利用其 select() 方法选中所有文本。
1 var test = document.getElementById("test"); 2 test.addEventListener("focus",function(){ 3 test.select(); 4 },false);
利用 keyup 事件检测用户输入新字符后,文本框内的字符串是否已经达到最大长度,若达到最大长度,则将焦点切换至下一个文本框。
1 // form4 自动切换焦点 2 (function() { 3 function nextFocus() { 4 var target = event.target; //target 促发keyup事件的表单元素 5 if (target.value.length === target.maxLength) { 6 for (let i = 0; i < form4.elements.length; i++) { 7 if (form4.elements[i] === target) { 8 if (form4.elements[i + 1]) { 9 form4.elements[i + 1].focus(); //获得焦点 10 } 11 return; 12 } 13 } 14 } 15 } 16 form4.addEventListener("keyup", nextFocus, false); 17 })();
HTML5 也规定了一组 JavaScript 属性方法,用来增强自定义表单验证机制。最常用的就是 setCustomValidity() 方法,基于这个方法可以针对特定字段编写自定义的验证逻辑,并强制利用 HTML5 的验证机制。
1 <form action="#"> 2 <p>评论框</p> 3 <textarea name="comment" id="comment" cols="30" rows="10" placeholder="写点什么吧"></textarea> 4 <br/> 5 <input type="submit" value="评论" /> 6 </form> 7 8 (function(){ 9 var comment = document.getElementById("comment"); 10 comment.oninput = function(){ //oninput事件自行查询 11 if(comment.value.length < 20){ 12 comment.setCustomValidity("客官,再多写点嘛!"); //当表单提交时会检查 13 }else{ 14 comment.setCustomValidity(""); 15 } 16 }; 17 })();
select选择框事件合集
1 <form id="form7" action="#"> 2 <select name="country" id="test"> 3 <option value="China">中国</option> 4 <option value="Amercian">美国</option> 5 <option value="England">英国</option> 6 <option value="Japan">日本</option> 7 <option value="Russia">俄罗斯</option> 8 </select> 9 </form> 10 <hr/> 11 <button id="btn1">显示所有的元素</button> 12 <button id="btn2">获得当前选中元素的索引</button> 13 <button id="btn3">添加新 option 到最后位置</button> 14 <button id="btn4">移除第一个 option</button> 15 16 var form7 = document.getElementById("form7"); 17 18 var btn1 = document.getElementById("btn1"); 19 var btn2 = document.getElementById("btn2"); 20 var btn3 = document.getElementById("btn3"); 21 var btn4 = document.getElementById("btn4"); 22 23 var select = form7.elements[0]; 24 btn1.onclick = function(){ 25 select.size = select.length; //所有选择都会显示 26 }; 27 btn2.onclick = function(){ 28 alert("当前索引:"+select.selectedIndex); 29 }; 30 var count=0; 31 btn3.onclick = function(){ 32 var newOption = new Option("国家 "+count,"Country "+count); //注意参数是 option(text,value) 33 count++; 34 select.add(newOption,undefined); //添加option 35 }; 36 btn4.onclick = function(){ 37 select.remove(0); 38 };
3.模块化js代码
通过在模块内部保留对公共API对象的内部引用,这样就可以在内部对模块实例进行修改,包括添加、删除方法和属性
1 function CoolModule(){ 2 var something = 'cool'; 3 var another = [1,2,3]; 4 function change() { 5 pubicAPI.doSomething = doAnother; 6 } 7 function doSomething(){ 8 console.log( something); 9 } 10 function doAnother(){ 11 console.log(another.join('!')); 12 } 13 var pubicAPI = { 14 change: change, 15 doSomething: doSomething 16 }; 17 return pubicAPI; 18 } 19 var foo = CoolModule(); 20 foo.doSomething(); //cool 21 foo.change(); 22 foo.doSomething(); //1!2!3 23 var foo1 = CoolModule(); 24 foo1.doSomething(); //cool
大多数模块依赖加载器或管理器,本质上都是将这种模块定义封装进一个友好的API
1 var MyModules = (function Manager() { 2 var modules = {}; 3 function define(name, deps, impl) { 4 for(var i=0; i<deps.length; i++){ 5 deps[i] = modules[deps[i]]; 6 } 7 modules[name] = impl.apply(impl,deps); //模块依赖impl函数 8 } 9 function get(name) { 10 return modules[name]; 11 } 12 return { 13 define: define, 14 get: get 15 }; 16 })();
以上代码的核心是modules[name] = impl.apply(impl,deps);
,为了模块的定义引入了包装函数(可以传入任何依赖),并且将模块的API存储在一个根据名字来管理的模块列表modules对象中;
1 MyModules.define('bar',[],function () { 2 function hello(who) { 3 return 'let me introduce: '+who; 4 } 5 return{ 6 hello: hello 7 }; 8 }); //module[bar] = {hello:hello} 9 MyModules.define('foo',['bar'],function (bar) { 10 var hungry = 'hippo'; 11 function awesome() { 12 console.log(bar.hello(hungry).toUpperCase()); 13 } 14 return { 15 awesome: awesome 16 }; 17 }); 18 var foo = MyModules.get('foo'); 19 foo.awesome();//LET ME INTRODUCE: HIPPO
CommonJS规范是服务器端Javascript模块规范。
Node.js的模块系统,就是参照CommonJS规范实现的。NPM也遵循了commonJS定义的包规范,从而形成了一套完整的生态系统。
CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}。require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。
1 //mathCommonJS.js 2 function MathClass() { 3 } 4 MathClass.PI = 3.14; 5 MathClass.E = 2.72; 6 MathClass.prototype.add = function(a, b) { 7 return a+b; 8 }; 9 module.exports = MathClass; 10 11 var MathClass = require('./mathCommonJS.js'); 12 Page( { 13 onLoad: function() { 14 console.log( "PI: " +MathClass.PI ); 15 var mathClass = new MathClass(); 16 console.log( "3 + 4: " +mathClass.add(3, 4) ); 17 } 18 });
AMD模块的写法
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
1 // math.js 2 define(function (){ 3 var add = function (x,y){ 4 return x+y; 5 }; 6 return { 7 add: add 8 }; 9 }); 10 11 // main.js 12 require(['math'], function (math){ 13 alert(math.add(1,1)); 14 });
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
1 define(['myLib'], function(myLib){ 2 function foo(){ 3 myLib.doSomething(); 4 } 5 return { 6 foo : foo 7 }; 8 });
4.函数式编程
Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions.
1 //把函数当成变量 2 var foo = (x) => console.log(x) 3 //把函数当作参数传入另一个函数 4 var bar = (y, fn) => fn(y) 5 foo('FP is good') // FP is good 6 bar('FP is great', foo) //FP is great
如果用函数式编程的思维遍历数组元素:
1 function forEach(arr, fn) { 2 for (let i = 0; i < arr.length; i++) { 3 fn(i, arr[i], arr) 4 } 5 } 6 7 forEach(arr, (index, value) => console.log(`index: ${index}, value: ${value}`))
废话少说,直接上干货
var animals = [ {name: 'a' , species: 'dog', weight: 11}, {name: 'b', species: 'cat', weight: 10}, {name: 'c', species: 'fish', weight: 1}, {name: 'd', species: 'cat', weight: 8}, {name: 'e', species: 'rabbit', weight: 3} ] // 找到所有种类为猫的动物 animals.filter((animal) => animal.species === 'cat') // [ { name: 'b', species: 'cat' }, { name: 'd', species: 'cat' } ] // 返回所有动物的名字 animals.map((animal) => animal.name) // [ 'a', 'b', 'c', 'd', 'e' ] // 最重动物的体重 这里的reduce方法参数要搞清楚 animals.reduce((pre, cur) =>(pre.weight>cur.weight?pre:cur)) //Object {name: "g", species: "dog", weight: 111}
给定一个字符串"abcdaabc"统计每个字符的出现次数.
1 // 一般做法是这样的 2 var str="abcdaabcffffgggggg" 3 var count = {}; 4 var i; 5 6 for(i=0;i<str.length;i++){ 7 var chr = arr.charAt(i); 8 if( typeof count[chr] === "undefined"){ 9 count[chr] = 1; 10 }else{ 11 count[chr]++; 12 } 13 } 14 15 // 利用函数式编程思想的方法是这样的 16 var res = str.split('') 17 .reduce((pre, cur) => (pre[cur]++ || (pre[cur] = 1), pre), {})
//Object {a: 3, b: 2, c: 2, d: 1, f: 4…}
统计文本中出现频率最高的十个单词
1 var fs = require('fs'); 2 var content = fs.readFileSync('words.txt').toString(); 3 var words = content.split(/[\s.,\/:\n]+/); 4 // 把单词全部变为小写并利用上一个例子的方法统计单词出现的次数 5 var tally = words.map((word) => word.toLowerCase()) 6 .reduce((pre, cur) => (pre[cur]++ || (pre[cur]=1), pre), {}) //和上面的例子一样 统计出出现次数 7 //把object的key变成数组并进行排序 8 var top10 = Object.keys(tally) 9 .map((key) => { 10 return {word: key, count: tally[key]} 11 }) 12 .sort((a, b) => b.count - a.count) //逆序排序 13 .slice(0, 10) 14 console.log(top10)