一、type of
返回值共有7種:undefined, object, boolean, number, string, symbol, function
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
- 宣告一個只讀的常量
2.1 使用var的注意事項:
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]
複製程式碼
由於變數宣告提升,這段程式碼相當於
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();
複製程式碼
2.2 使用let的注意事項
- 塊級作用域
- 同一個作用域下只能宣告一次
- 暫存死區(temporal dead zone)
- 迴圈中的let作用域
- 瀏覽器相容性
塊級作用域示例:
{
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
複製程式碼
暫存死區示例:
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)
}
複製程式碼
2.2 使用const的注意事項
- const宣告的變數不可以再次被改變
- 其他使用與let一樣
示例:
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
複製程式碼
注意:給變數賦值為物件時,物件引用不能變,但是物件裡的屬性值可以改變。
// 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}
複製程式碼
三、嚴格模式-"use strict";
(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
複製程式碼
四、抽象相等
4.1嚴格相等於非嚴格相等
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]
複製程式碼
五、Boolean 型別
- true 和 false
- 0、''、null、undefined 被隱式轉換為 false,其他轉為 true
- 建議採用嚴格比較,可以通過 !! 將非 boolean 值轉為 boolean
- 布林操作符 && 和 || 並不會轉換型別
- && 和 || 的短路特性
- 比較操作符總是返回 boolean 型別
5.1 Boolean型別的應用
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)';
});*/
}
複製程式碼
六、Number
- 整數 -2^53 ~ 2^53
- 小數精度 Number.EPSILON
- Infinity、Number.MAX_VALUE、Number.MIN_VALUE
- 浮點數有精度問題
- 2進位制,8進位制,十六進位制
- +0和-0
6.1 Number型別的應用
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
複製程式碼
七、String
- 引號規範
- 轉義字元
- 字串與字元
- 字串與型別轉換
- 常用字串操作
- 模板字串
7.1 引號規範與轉義
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);
}"
"奇舞團"
複製程式碼
7.2 處理字元
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"
複製程式碼
7.3 字串型別轉換
7.3.1 隱式型別轉換
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"
複製程式碼
7.5 常用字串操作
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!"
複製程式碼
7.6 模板字串(ES6)
const tpl1 = `我所做的餡餅
是全天下
最好吃的`;
console.log([tpl1, typeof tpl1]);
{
let who = '月影', what = '月餅';
const tpl2 = `${who}所做的${what}
是全天下
最好吃的`;
console.log(tpl2);
}
複製程式碼
["我所做的餡餅
是全天下
最好吃的", "string"]
"月影所做的月餅
是全天下
最好吃的"
複製程式碼
7.7 模板字串高階用法
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>
"
複製程式碼
八、Object
- 物件的屬性
- 值和引用
- 型別與構造器
- 內建型別
- 物件的高階屬性
- class
8.1 物件的屬性
- 屬性名規則:可以是有效字串或者任意可轉換為有效字串的型別
- 屬性的訪問和遍歷
8.2 物件建立
//建立物件
{
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);
}
複製程式碼
8.3 屬性訪問
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]
複製程式碼
8.4 屬性訪問
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);
}]
複製程式碼
8.5 值和引用
- 值型別與引用型別
- 基本型別對應的包裝型別
- 物件的拷貝
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"]
複製程式碼
十一、物件的型別和構造器
- new 和 constructor
- prototype
- instanceOf
- ES6 class
//假設 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
* }
*/
複製程式碼
12.1 原型鏈有什麼弊端?
設計原型鏈是為了程式碼複用,但是原型鏈有額外構造器呼叫的問題。
12.2 問題:額外的構造器呼叫?
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]
// 注意:這裡呼叫了兩次構造器
複製程式碼
解決方法 1:通用化父類構造器
//原型繼承
/**
* 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]
複製程式碼
註釋:但這並不總是好的,尤其是在多重繼承的時候
解決方法 2:延遲父類構造器呼叫
//原型繼承
/**
* 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]
複製程式碼
十三、javascript內建型別
- Object
- Function
- Array
- Date
- Regex
- Error
- Math
- ArrayBuffer
- Promise
- DataView
- Map
- Set
- TypedArray // Uint16Array(buffer) Uint8Array(buffer)
- Proxy(ES6)// 處理非同步
十四、為什麼不建議修改 Object.prototype 和 Array.prototype
14.1 在陣列上新增方法的問題
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的過濾掉,它會把陣列上可列舉的屬性都遍歷出來。
複製程式碼
解決這一問題的方法
- 使用 ES5 新特性 defineProperty
- 規定禁止使用 for...in 運算元組,只用 for 或 Array.prototype.forEach
- 規定禁止往陣列上隨便新增方法
示例:
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"
*/
複製程式碼
14.2 getter / setter
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]
複製程式碼
14.3 雙向繫結
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);
複製程式碼
歡迎關注
-
文章內容來源:360奇舞團前端星課程。
-
奇舞週刊: weekly.75team.com/
-
部落格:
- 奇舞團部落格:75team.com/
- 月影的部落格:www.h5jun.com/
- 屈屈的部落格:imququ.com/