资讯专栏INFORMATION COLUMN

JavaScript - 变量、值、类型

ziwenxie / 1988人阅读

摘要:一返回值共有种二变量声明方法声明一个变量,可以将其初始化为一个值声明一个块级作用域变量,可以将其初始化一个值声明一个只读的常量使用的注意事项不支持块级作用域存在变量提升举例由于变量声明提升,这段代码相当于使用的注意事项块级作用域

一、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 = "

This is a paragraph

"; console.log(text); console.log(html); var text2 = "我所做的馅饼 是全天下 最好吃的"; console.log(text2); var text3 = "if(a){ console.log(b); }"; console.log(text3); var text4 = "u5947u821eu56e2"; console.log(text4);

Console

"This is a text."
"

This is a paragraph

" "我所做的馅饼 是全天下 最好吃的" "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) => `
  
    ${data.map(item=>`
  • 姓名:${item.name}年龄:${item.age}
  • `).join("")}
`; let data = [ {name: "Akira", age: 35}, {name: "Jane", age: 26}, {name: "Jhon", age: 54} ]; console.log(formatedList(data));

Console

"3 + 5 is 8"
"
  
  • 姓名:Akira年龄:35
  • 姓名:Jane年龄:26
  • 姓名:Jhon年龄:54
"
八、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 在数组上添加方法会被for...in枚举出来
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);

演示 demo

欢迎关注

文章内容来源:360奇舞团前端星课程。

奇舞周刊: https://weekly.75team.com/

博客:

奇舞团博客:https://75team.com/

月影的博客:https://www.h5jun.com/

屈屈的博客:https://imququ.com/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98220.html

相关文章

  • JavaScript学习第一天笔记(变量、常量、数据类型

    摘要:变量与常量变量定义变量是存储数据信息的容器。中,变量是弱类型,也被称为松散类型。三类型布尔类型只有两个值均为小写如果大写活着大小写混合,识别为另一个变量名称。 JavaScript简介 JavaScript是一门跨平台、面向对象的轻量级脚本语言,运行于JavaScript解释器或者引擎中。JavaScript中,包括三个主要的部分1.核心语法内容(ECMAScript)2.文档对象模型...

    tinysun1234 评论0 收藏0
  • JavaScript基础语法 变量 常量 数据类型

    摘要:第十六天笔记基础语法区分大小写是一种区分大小写的语言的关键字变量名函数名以及任何其他的标识符必须适应一致的大小写形势卧龙前端空格和换行并不是的语法内容可有可无建议使用将代码书写的更加整洁美观更有可读性可选的分号分号可选项语法并不是强制要 第十六天笔记 JavaScript 基础语法 区分大小写 JavaScript是一种区分大小写的语言 JavaScript的关键字 变量名 函...

    xingpingz 评论0 收藏0
  • JavaScript的个人分享

    摘要:的基础语法的语法是区分大小写的注意在中是区分大小写的变量名称不能是中文代码示例一花一世界控制台调用效果图语法中的空格与换行注意并不是的语法内容可有可无的建议使用因为这样会使代码看的更清晰利于阅读代码示例代码一花一世界中的分号可选项语法并 JavaScript的基础语法: JavaScript的语法是区分大小写的 注意: 在JavaScript中是区分大小写的 变量名称不能是中文 代...

    JasonZhang 评论0 收藏0
  • JavaScript入门

    摘要:被识别为结束符。变量时被认为时有名字的容器。常量一旦被声明初始化,值并不能被改变。字符串可以看成右字符组成的数组,可能被误认为可变的。表达式没有显示的返回任何内容。 Java script 简介 Javascript 是一门跨平台,面向对象的轻量级脚本语言,运行于Javascript解释权/引擎 avascript内置了一个包含一系列对象的标准库,比如数组,日期,数字和一个语言元素合...

    AaronYuan 评论0 收藏0
  • JavaScript学习总结(一)基础部分

    摘要:前缀规范每个局部变量都需要有一个类型前缀,按照类型可以分为表示字符串。例如,表示以上未涉及到的其他对象,例如,表示全局变量,例如,是一种区分大小写的语言。布尔值与字符串相加将布尔值强制转换为字符串。 基本概念 javascript是一门解释型的语言,浏览器充当解释器。js执行时,在同一个作用域内是先解释再执行。解释的时候会编译function和var这两个关键词定义的变量,编译完成后从...

    AlanKeene 评论0 收藏0
  • JavaScript 闯关记》之变量和数据类型

    摘要:当程序需要将值保存起来以备将来使用时,便将其赋值给一个变量,值的类型称作数据类型。运算符鉴于是松散类型的,因此需要有一种手段来检测给定变量的数据类型,就是负责提供这方面信息的运算符。 当程序需要将值保存起来以备将来使用时,便将其赋值给一个变量,值的类型称作数据类型。 变量 JavaScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个...

    HackerShell 评论0 收藏0

发表评论

0条评论

ziwenxie

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<