返回值共有7种:undefined, object, boolean, number, string, symbol, functionjavascript
typeof undefined === 'undefined'; typeof true === 'boolean'; typeof 123 === 'number'; typeof 'username' === 'string'; typeof {team : '75team'} === 'object' typeof symbol() === 'symbol'; typeof null === 'object'; typeof function() {} === 'function';
var
- 声明一个变量,能够将其初始化为一个值 const
- 声明一个块级做用域变量,能够将其初始化一个值 let
- 声明一个只读的常量html
var
不支持块级做用域var
存在变量提高举例:前端
console.log(a === undefined); var a = 10; function foo(){ console.log([a, i]); var a = 20; for(var i = 0; i < a; i++){ //do sth. } console.log([a, i]); } foo();
//---console: true [undefined, undefined] [20, 20]
因为变量声明提高,这段代码至关于java
var a; console.log(a === undefined); a = 10; function foo(){ var a, i; console.log([a, i]); a = 20; for(i = 0; i < a; i++){ //do sth. } console.log([a, i]); } foo();
块级做用域示例:mysql
{ let x = 10; console.log('x is ' + x); } console.log(typeof x); let i = 20; for(let i = 0; i < 10; i++){ console.log(i); } console.log(i); var y = 3; var y = 4; console.log(y); // let z = 3; // var z = 4; // console.log(z);
//---console: "x is 10" "undefined" 0 1 2 3 4 5 6 7 8 9 20 4
暂存死区示例:sql
let x = 10; function foo(){ console.log(x); let x = 20; //若是这一句改为 var 会怎样? return x * x; } console.log(foo());
//---console: "ReferenceError: x is not defined"
循环中的let做用域示例:数组
var buttons = document.querySelectorAll('button'); for(var i = 0; i < buttons.length; i++){ buttons[i].onclick = evt => console.log('点击了第 ' + i + '个按钮'); }
//---console: "点击了第 5个按钮" "点击了第 5个按钮" "点击了第 5个按钮" "点击了第 5个按钮"
这里是由于click这个方法是异步的,只有在点击以后,js才会去执行这段代码,这时for循环中的 i 已经循环完,因此会产生点击哪一个按钮都返回 5 。解决这类问题常利用‘闭包’,或者使用
let
var buttons = document.querySelectorAll('button'); for(var i = 0; i < buttons.length; i++){ (function (i) { buttons[i].onclick = evt => console.log('点击了第 ' + i + '个按钮'); })(i) }
示例:浏览器
const BUFFER_SIZE = 1024; let buffer = new ArrayBuffer(BUFFER_SIZE); console.log(buffer); let data = new Uint16Array(buffer); let data2 = new Uint8Array(buffer); data[0] = 0xff06; console.log(data2[0], data2[1]);
//---console: [object ArrayBuffer] 6 255
注意:给变量赋值为对象时,对象引用不能变,可是对象里的属性值能够改变。bash
// A的地址没有改变,这样也是能够的。 const A = {a: 1}; A.a = 2; console.log(A); // A: {a: 2} // 如何不让A被操做呢? const A = Object.freeze({a: 1}); A.a = 2; console.log(A); // A: {a: 1}
(function(){ //'use strict'; var x = y = 0; //do sth. })(); console.log([typeof x, typeof y]);
//---console: ["undefined", "number"]
可是在ES6与‘use strict’某些时候会有冲突闭包
(function f( j = 777 ) { 'use strict'; // do sth. }) ERROR: Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list
javascript
console.log([null !== undefined, null == undefined]); //true, true console.log(['1' == 1, [] == 0, '' == 0, 0 == false, 1 == true]); console.log([NaN != NaN]);
Console
[true, true] [true, true, true, true, true] [true]
javascript
var result = 1 && 2; console.log([result, !!result]); var list = [1,,2,,,4,5,0,9]; list = list.filter(item => item); console.log(list); function showTip(tip){ tip = tip || 'This is a tip'; console.log(tip); var label = document.createElement('label'); label.className = 'tip'; label.innerHTML = tip; document.body.appendChild(label); } document.querySelector('button').onclick = showTip.bind(null, '');
console
"This is a tip" "This is a tip" "This is a tip" ...
示例2:
JavaScript
/** * options -> type:x、y、xy or function */ function applyAnimate(el, duration, options, easing){ var startTime = Date.now(); if(typeof el === 'string'){ el = document.querySelector(el); } duration = duration || 1000; options = options || { property: 'x', distance: 100 }; easing = easing || function(p){return p;}; requestAnimationFrame(function update(){ var now = Date.now(); var p = (now - startTime)/duration; var ep = easing(p); if(typeof options !== 'function'){ var attr = options.property, distance = options.distance; var translate = []; if(attr.indexOf('x') >= 0){ translate.push('translateX(' + distance * ep + 'px)'); } if(attr.indexOf('y') >= 0){ translate.push('translateY(' + distance * ep + 'px)'); } el.style.transform = translate.join(' '); }else{ options(el, ep, p); } if(p <= 1){ requestAnimationFrame(update); } }); } document.querySelector('.ball').onclick = function(){ applyAnimate('.ball'); /*applyAnimate('.ball', 1000, function(el, ep, p){ const distance = 100, pi2 = 2 * Math.PI; el.style.transform = 'translate(' + distance * Math.cos(pi2 * ep) + 'px,' + distance * Math.sin(pi2 * ep) + 'px)'; });*/ }
javascript
//----------- console.log(0.2 + 0.4); //WTF!! IEEE754 console.log(((0.2 + 0.4) * 100).toPrecision(2)); console.log(((0.2 + 0.4) * 100).toFixed(2)); //---------- const ball = document.getElementById('ball'); var distance = 100, duration = 2000; ball.onclick = function(){ var startTime = Date.now(); requestAnimationFrame(function update(){ var p = (Date.now() - startTime) / duration; ball.style.transform = 'translateX(' + distance * p + 'px)' console.log(p,distance); if(p <= 1){ //不该当用相等比较浮点数!! requestAnimationFrame(update); } }); }
console
0.6000000000000001 "60" "60.00"
示例2:
JavaScript
console.log([Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]); var bigInteger = Number.MAX_SAFE_INTEGER + 1; console.log([bigInteger, bigInteger + 1, bigInteger + 2]); //WTF!! console.log(1234567890000000000000000000); //科学计数法 console.log(Math.pow(2, 53) === bigInteger); console.log([Number.isSafeInteger(bigInteger), Number.isSafeInteger(bigInteger - 1)]); //----------- console.log([Number.MAX_VALUE, Number.MIN_VALUE]); console.log([Number.MAX_VALUE + 1, Number.MAX_VALUE * 2]); console.log([Number.EPSILON]); console.log(0.99 - 1e-17 === 0.99); console.log(0.99 - Number.EPSILON === 0.99);
console
[9007199254740991, -9007199254740991] [9007199254740992, 9007199254740992, 9007199254740994] 1.23456789e+27 true [false, true] [1.7976931348623157e+308, 5e-324] [1.7976931348623157e+308, Infinity] [2.220446049250313e-16] true false
javascript
var text = 'This is a text.'; //规范建议默认使用双引号 var html = '<p class="sth">This is a <em>paragraph</em></p>'; console.log(text); console.log(html); var text2 = '我所作的馅饼\n是全天下\n最好吃的'; console.log(text2); var text3 = 'if(a){\n\tconsole.log(b);\n}'; console.log(text3); var text4 = '\u5947\u821e\u56e2'; console.log(text4);
Console
"This is a text." "<p class=\"sth\">This is a <em>paragraph</em></p>" "我所作的馅饼 是全天下 最好吃的" "if(a){ console.log(b); }" "奇舞团"
JavaScript
var str = 'my string'; console.log(str.charAt(5)); var charArray = Array.from(str); //str.split(''); console.log(charArray); console.log(str.charCodeAt(5), str.charCodeAt(6)); console.log(String.fromCharCode(114, 105)); console.log(String.fromCharCode(...charArray.map(c=>c.charCodeAt(0))));
Console
"r" ["m", "y", " ", "s", "t", "r", "i", "n", "g"] 114 105 "ri" "my string"
console.log([1+2, '1'+2, '1'-2]); // console: [3, "12", -1] // 注释:减法不会发生隐式类型转换,能够用减零的方式将字符串转化成数字,如:console.log('1'- 0 + 2);
console.log(parseInt('100abc', 2), Number('0b100')); // console: 4 4 // 转整数
console.log(parseFloat('12.3e10xx')); // console: 123000000000 // 转浮点数, parseFloat会尽量的将字符串中的值转化成浮点数,而后将转化不了的字符串忽略,这里是将“xx”省略。
// 封装对象的 toString 方法 var foo = { toString(){ return 'foo'; } }; console.log(foo + ' bar'); // console: "foo bar"
javascript
const a = 'hello'; const b = 'WORLD'; const c = '!'; console.log(a + ' ' + b + c); //字符串链接 // 注释:字符串能够用“+”相加,但在某些浏览器内性能很差,可采用数组的join方法拼接字符串。 // console.log([a, b, c].join('')); console.log(a.toUpperCase() + ' ' + b.toLowerCase() + c); //转大小写 console.log(a.split('').reverse().join('')); //逆序字符串 console.log([a.slice(2,3), a.substr(2,3)]); //截取子串 // 注释: slice和substr的参数含义不同 const d = a + ' ' + b + c; console.log(d.indexOf(b)); //字符串查找 console.log(d.replace(a, a.toUpperCase()));
Console
"hello WORLD!" "HELLO world!" "olleh" ["l", "llo"] 6 "HELLO WORLD!"
const tpl1 = `我所作的馅饼 是全天下 最好吃的`; console.log([tpl1, typeof tpl1]); { let who = '月影', what = '月饼'; const tpl2 = `${who}所作的${what} 是全天下 最好吃的`; console.log(tpl2); }
["我所作的馅饼 是全天下 最好吃的", "string"] "月影所作的月饼 是全天下 最好吃的"
JavaScript
let output = (a, b) => `${a} + ${b} is ${a + b}`; console.log(output(3, 5)); let formatedList = (data) => ` <ul> ${data.map(item=>` <li><span>姓名:${item.name}</span><span>年龄:${item.age}</span></li> `).join('')} </ul> `; let data = [ {name: 'Akira', age: 35}, {name: 'Jane', age: 26}, {name: 'Jhon', age: 54} ]; console.log(formatedList(data));
Console
"3 + 5 is 8" " <ul> <li><span>姓名:Akira</span><span>年龄:35</span></li> <li><span>姓名:Jane</span><span>年龄:26</span></li> <li><span>姓名:Jhon</span><span>年龄:54</span></li> </ul> "
//建立对象 { let myObj = new Object(); myObj.name = 'akira'; myObj.birthday = '12-29'; console.log(myObj); } //然而上面这么写的是菜鸟,普通青年这么写 { let myObj = { name: "akira", birthday: "12-29" }; console.log(myObj); } //有些二逼青年: { let myObj = Object.create({name: "akira", birthday: "21-29"}); console.log(myObj); }
javascript
// 对象属性是有效字符串,属性访问能够经过.和[] { let myObj = { name: "akira", birthday: "12-29" }; console.log([myObj.name, myObj['birthday']]); } { // []属性访问的好处是能够计算 const conf = { adapter: 'sqlite', db: { sqlite: { //... }, mysql: { //... } } } let dbSetting = conf.db[conf.adapter]; } { // 在 ES6 中字面量的 key 也支持属性计算 // 好比能够给对象定义一个变量key let process = {env: {}}; const ENV = process.env.JSBIN_ENV || 'development'; const conf = { [ENV]: true }; console.log([conf.development, conf.production]); //ES5中只能这样实现: var ENV = process.env.JSBIN_ENV || 'development'; var conf = {}; conf[ENV] = true; console.log([conf.development, conf.production]); }
Console
["akira", "12-29"] [true, undefined]
javscript
let point = { x : 100, y : 100, getLength : function(){ let {x, y} = this; return Math.sqrt(x * x + y * y); } } console.log(point.getLength()); //用 for...in 遍历 for(let key in point){ console.log([key, point[key]]); } //用 Object.keys 遍历 Object.keys(point).forEach((key) => console.log([key, point[key]]));
Console
141.4213562373095 ["x", 100] ["y", 100] ["getLength", function(){ let {x, y} = this; return Math.sqrt(x * x + y * y); }] ["x", 100] ["y", 100] ["getLength", function(){ let {x, y} = this; return Math.sqrt(x * x + y * y); }]
let x = 20, y = 30; function foo(a, b){ a++; b++; console.log([a, b]); } foo(x, y); console.log([x, y]); // 注意:Number是值类型,当把x,y做为参数传递给function的时候,只是传递了x,y的副本,因此执行完foo(x,y), x,y的值并无变化。 const obj = {x: 20, y: 30}; function foo2(obj){ obj.x++; obj.y++; console.log(obj); } foo2(obj); console.log(obj); // 注意:Object是引用类型,它是将obj里的引用自加,因此函数执行完,obj里的值会发生变化。
Console
[21, 31] [20, 30] [object Object] { x: 21, y: 31 } [object Object] { x: 21, y: 31 }
var str = "Hello World"; var strObj = new String(str); // 注释:若是写new是包装类型,若是不写new是值类型 console.log([str, strObj]); // console: ["Hello World", "Hello World"] console.log([typeof str, typeof strObj]); // console: ["string", "object"] console.log([str instanceof String, strObj instanceof String]); // console: [false, true] var n = new Number(10); console.log([typeof n, typeof ++n]); // console: ["object", "number"] console.log(Object.prototype.toString.call(10)); // console: [object Number]
let conf = { adapter: 'sqlite', db: { sqlite: { name: 'xxx.sqlite' }, mysql: { name: 'xxx', username: 'work', passwd: '******' } } }; //直接引用 let conf2 = conf; conf2.adapter = 'mysql'; console.log(conf.adapter); // console: "mysql" //ES5 浅拷贝 conf.adapter = 'sqlite'; let copied = Object.assign({}, conf); copied.adapter = 'mysql'; console.log(conf.adapter); // console: "sqlite" console.log(copied.adapter); // console: "mysql" copied.db.sqlite.name = 'yyy.sqlite'; console.log(conf.db.sqlite.name); // console: "yyy.sqlite" //深拷贝 function deepCopy(des, src){ for(var key in src){ let prop = src[key]; if(typeof prop === 'object'){ des[key] = des[key] || {}; deepCopy(des[key], prop); }else{ des[key] = src[key]; } } return des; } let deepCopied = deepCopy({}, conf); deepCopied.db.sqlite.name = 'zzz.sqlite'; console.log([deepCopied.db.sqlite.name, conf.db.sqlite.name]); // console: ["zzz.sqlite", "yyy.sqlite"]
//假设 JS 没有 "new" 操做符,咱们该如何实现建立对象? function defineClass(initializer, proto){ return function f(...args){ let obj = Object.create(proto); //f.prototype = proto; //just let instanceof make sense obj.constructor = initializer; obj.constructor(...args); return obj; } } var Point = defineClass(function(x, y){ this.x = x; this.y = y; }, { getLength: function(){ let {x, y} = this; return Math.sqrt(x * x + y * y); } }); var p = Point(3, 4); console.log([p.getLength(), p instanceof Point, p instanceof Object]); //因此 'new' 实际上是一种语法糖
// __proto__ 暴力构建原型链 var a = {x : 1}, b = {y : 2}, c = {z : 3}; b.__proto__ = a; c.__proto__ = b; console.log(c); /** * console: * [object Object] { * x: 1, * y: 2, * z: 3 * } */
//使用 Object.create 构建原型链 var a = {x : 1}; var b = Object.create(a); // b继承对象a b.y = 2; var c = Object.create(b); // c继承对象b c.z = 3; console.log(c); /** * console: * [object Object] { * x: 1, * y: 2, * z: 3 * } */
//使用构造器方式 function A() { this.x = 1; } function B() { this.y = 2; } B.prototype = new A(); function C() { this.z = 3; } C.prototype = new B(); var c = new C(); console.log(c); /** * console: * [object Object] { * x: 1, * y: 2, * z: 3 * } */
设计原型链是为了代码复用,可是原型链有额外构造器调用的问题。
javascript
//原型继承 /** * abstract point */ function Point(components){ console.log('Point constructor called'); this.components = components; } Point.prototype = { getDimension: function(){ return this.components.length; }, getLength: function(){ var sum = 0, components = this.components; for(var i = 0; i < components.length; i++){ sum += Math.pow(components[i], 2); } return Math.sqrt(sum); } }; function Point2D(x, y){ Point.call(this, [x, y]); } Point2D.prototype = new Point(); Point2D.prototype.getXY = function(){ var components = this.components; return { x: components[0], y: components[1] }; }
Console
"Point constructor called" "Point constructor called" ["(3,4)", 5, true] // 注意:这里调用了两次构造器
//原型继承 /** * abstract point */ function Point(dimension){ console.log('Point constructor called'); this.dimension = dimension; } Point.prototype = { setComponents: function(){ components = [].slice.call(arguments); if(this.dimension !== components.length){ throw new Error('Dimension not match!'); } this.components = components; }, getDimension: function(){ return this.dimension; }, getLength: function(){ var sum = 0, components = this.components; for(var i = 0; i < components.length; i++){ sum += Math.pow(components[i], 2); } return Math.sqrt(sum); } }; function Point2D(x, y){ this.setComponents(x, y); } Point2D.prototype = new Point(2); Point2D.prototype.getXY = function(){ var components = this.components; return { x: components[0], y: components[1] }; } Point2D.prototype.toString = function(){ return '(' + this.components + ')'; } var p = new Point2D(3, 4); console.log([p+'', p.getLength(), p instanceof Point]);
Console
"Point constructor called" ["(3,4)", 5, true]
注释:但这并不老是好的,尤为是在多重继承的时候
//原型继承 /** * abstract point */ function Point(components){ console.log('Point constructor called'); this.components = components; } Point.prototype = { getDimension: function(){ return this.components.length; }, getLength: function(){ var sum = 0, components = this.components; for(var i = 0; i < components.length; i++){ sum += Math.pow(components[i], 2); } return Math.sqrt(sum); } }; function Point2D(x, y){ Point.call(this, [x, y]); } Point2D.prototype = Object.create(Point.prototype); //function PointT(){}; //PointT.prototype = Point.prototype; //Point2D.prototype = new PointT(); Point2D.prototype.getXY = function(){ var components = this.components; return { x: components[0], y: components[1] }; } Point2D.prototype.toString = function(){ return '(' + this.components + ')'; } var p = new Point2D(3, 4); console.log([p+'', p.getLength(), p instanceof Point]);
Console
"Point constructor called" ["(3,4)", 5, true]
Array.prototype.remove = function(item) { var idx = this.indexOf(item); if(idx >= 0){ return this.splice(idx, 1)[0]; } return null; } var arr = [1, 2, 3]; arr.remove(2); //perfect?? console.log(arr); for(var i in arr){ if(!Number.isNaN(i-0)){ console.log(i + ':' + arr[i]); }else{ console.log(i + '是什么鬼?'); } } /** * [1, 3] * "0:1" * "1:3" * "remove是什么鬼?" */ // 注释:利用for in遍历数组或对象的好处是能够把值为undefined的过滤掉,它会把数组上可枚举的属性都遍历出来。
示例:
Object.defineProperty(Array.prototype, 'remove', { value : function(item) { var idx = this.indexOf(item); if(idx >= 0) { return this.splice(idx, 1); } return null; }, // enumerable: true // 当且仅当该属性的enumerable为true时,该属性才可以出如今对象的枚举属性中。默认为 false。 }); var arr = [1, 2, 3]; arr.remove(2); console.log(arr); for(var i in arr) { if(!Number.isNaN(i-0)) { console.log(i + ':' + arr[i]); }else{ console.log(i + '是什么鬼?'); } } /** * console: * [1, 3] * "0:1" * "1:3" */
function Point2D(x, y){ this.x = x; this.y = y; } Object.defineProperty(Point2D.prototype, 'length', { get: function() { let {x, y} = this; return Math.sqrt(x * x + y * y); }, set: function(len) { let arc = Math.atan2(this.y, this.x); this.x = len * Math.cos(arc); this.y = len * Math.sin(arc); } }); Object.defineProperty(Point2D.prototype, 'arc', { get: function(){ let {x, y} = this; return 180 * Math.atan2(y, x) / Math.PI; }, set: function(arc){ arc = Math.PI * arc / 180; let len = this.length; this.x = len * Math.cos(arc); this.y = len * Math.sin(arc); } }); let p = new Point2D(1, 1); console.log([p.length, p.arc]); // [1.4142135623730951, 45] p.length *= 2; console.log([p.x, p.y]); // [2.0000000000000004, 2] p.arc = 90; console.log([p.x, p.y]); // [1.7319121124709868e-16, 2.8284271247461903]
const view = { nameEl: document.getElementById('name'), ageEl: document.getElementById('age'), submitBtn: document.getElementById('confirm') } view.submitBtn.addEventListener('click', function(evt){ console.log('你要提交的数据是:' + [user.name, user.age]); evt.preventDefault(); }); function User(name, age){ this.name = name; this.age = age; } User.prototype.bind = function(view){ view.nameEl.addEventListener('change', evt => { this.name = evt.target.value; }); view.ageEl.addEventListener('change', evt => { this.age = evt.target.value; }); } Object.defineProperty(User.prototype, 'name', { set: function(name){ view.nameEl.value = name; }, get: function(){ return view.nameEl.value; } }); Object.defineProperty(User.prototype, 'age', { set: function(name){ var ageOptions = Array.from(view.ageEl.options) .map(item => item.innerHTML); if(ageOptions.indexOf(name) === '-1'){ throw new Error('无效的年龄格式'); } view.ageEl.value = name; }, get: function(){ return view.ageEl.value; } }); var user = new User('akira', '80后'); user.bind(view);
博客: