返回值共有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);
博客: