amenzai / myDiary

Record what you do every day
4 stars 0 forks source link

JS碎碎谈 | 2018-05-06 #33

Closed amenzai closed 5 years ago

amenzai commented 6 years ago

字符串

是个特殊的数组

var str = 'hello'
str[0] // h

想要使用数组方法需要借助call方法。 unicode编码 \n 反斜杠转义 base64编码:btoa()编码 atob()解码 中文在进行base编码时,先用encodeURIComponent()进行编码,解码一样

对象

创建方法:

var o1 = {};
var o2 = new Object();
var o3 = Object.create(Object.prototype); //用于继承

其他,大括号问题 eval('{foo: 123}') // 123 eval('({foo: 123})') // {foo: 123}

检查变量是否声明: if ('a' in window) { // 变量 a 声明过 } else { // 变量 a 未声明 }

查看对象属性:Object.keys(obj) 删除属性:delete //不能删除继承属性(不可枚举)

var o = Object.defineProperty({}, 'p', {
  value: 123,
  configurable: false
});

o.p // 123
delete o.p // false

in运算符检查对象是否包含对应属性 不能识别继承的,可以用hasOwnproperty()

var o = { p: 1 };
'p' in o // true

for in 循环遍历对象 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性 它不仅遍历对象自身的属性,还遍历继承的属性(使用构造函数和原型创建的对象)。

with语句 它的作用是操作同一个对象的多个属性时,提供一些书写的方便。

// 例一
with (o) {
  p1 = 1;
  p2 = 2;
}
// 等同于
o.p1 = 1;
o.p2 = 2;

单纯从上面的代码块,根本无法判断x到底是全局变量,还是o对象的一个属性。 这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。 因此,建议不要使用with语句,可以考虑用一个临时变量代替with。

with(o1.o2.o3) {
  console.log(p1 + p2);
}

// 可以写成

var temp = o1.o2.o3;
console.log(temp.p1 + temp.p2);

数组

数组是个特殊的对象 伪数组转为数组 var arr = Array.prototype.slice.call(arrayLike); 数组空位 var arr = [1,,2] 空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。

函数

给函数参数设置默认值

function f(a){
  a = a || 1;
  return a;
}

f('') // 1
f(0) // 1

改进

function f(a) {
  (a !== undefined && a !== null) ? a = a : a = 1;
  return a;
}

f() // 1
f('') // ""
f(0) // 0

函数参数传递分为:传值传递、传址传递。 函数参数获取的是一个拷贝

arguments

// 用于apply方法
myfunction.apply(obj, arguments).

// 使用与另一个数组合并
Array.prototype.concat.apply([1,2,3], arguments)

变为真正的数组

var args = Array.prototype.slice.call(arguments);

// or

var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}

闭包

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。 请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

闭包的另一个用处,是封装对象的私有属性和私有方法。

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

立即执行函数

var = function(){}(); (function(){})(); +function(){}();

eval函数

将字符串当做语句执行

操作符

void(0)返回undefined

<a href="javascript:void window.open('http://example.com/')">
  点击打开新窗口
</a>
  <a href="javascript:void(0);"></a>

数据类型转换

Number()

// 数值:转换后还是原来的值
Number(324) // 324

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324

// 字符串:如果不可以被解析为数值,返回NaN
Number('324abc') // NaN

// 空字符串转为0
Number('') // 0

// 布尔值:true 转成1,false 转成0
Number(true) // 1
Number(false) // 0

// undefined:转成 NaN
Number(undefined) // NaN

// null:转成0
Number(null) // 0

Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。

parseInt('42 cats') // 42
Number('42 cats') // NaN

String() Boolean()

错误处理机制

Error对象

JavaScript解析或执行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript原生提供一个Error构造函数,所有抛出的错误都是这个构造函数的实例。

var err = new Error('出错了');
err.message // "出错了"

JavaScript的原生错误类型

  1. SyntaxError 语法错误

  2. ReferenceError ReferenceError是引用一个不存在的变量时发生的错误。

  3. TypeError TypeError是变量或参数不是预期类型时发生的错误

  4. URIError URIError是URI相关函数的参数不正确时抛出的错误

可以人为设置错误信息

new Error('出错了!');
new RangeError('出错了,变量超出有效范围!');
new TypeError('出错了,变量类型无效!');

实例的message属性获取

自定义错误

function UserError(message) {
   this.message = message || "默认信息";
   this.name = "UserError";
}

UserError.prototype = new Error();
UserError.prototype.constructor = UserError;

throw语句

// 抛出一个字符串
throw "Error!";

// 抛出一个数值
throw 42;

// 抛出一个布尔值
throw true;

// 抛出一个对象
throw {toString: function() { return "Error!"; } };

JavaScript引擎一旦遇到throw语句,就会停止执行后面的语句,并将throw语句的参数值,返回给用户。

try catch 结构

为了对错误进行处理,需要使用try...catch结构。

var n = 100;

try {
  throw n;
} catch (e) {
  if (e <= 50) {
    // ...
  } else {
    throw e;
  }
}

finally代码块

try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句。

openFile();

try {
  writeFile(Data);
} catch(e) {
  handleError(e);
} finally {
  closeFile();
}

内置对象

Object

  1. Object()
Object() // 返回一个空对象
Object() instanceof Object // true

Object(undefined) // 返回一个空对象
Object(undefined) instanceof Object // true

Object(null) // 返回一个空对象
Object(null) instanceof Object // true

Object(1) // 等同于 new Number(1)
Object(1) instanceof Object // true
Object(1) instanceof Number // true

Object('foo') // 等同于 new String('foo')
Object('foo') instanceof Object // true
Object('foo') instanceof String // true

Object(true) // 等同于 new Boolean(true)
Object(true) instanceof Object // true
Object(true) instanceof Boolean // true

如果Object方法的参数是一个对象,它总是返回原对象。

var arr = [];
Object(arr) // 返回原数组
Object(arr) === arr // true

var obj = {};
Object(obj) // 返回原对象
Object(obj) === obj // true

var fn = function () {};
Object(fn) // 返回原函数
Object(fn) === fn // true
  1. Object 对象的静态方法 Object.keys():不包含不可枚举类型属性 Object.getOwnPropertyNames():包含 但两者都不包货继承属性

  2. Object.prototype valueOf():返回当前对象对应的值。 toString():返回当前对象对应的字符串形式。 toLocaleString():返回当前对象对应的本地字符串形式。 hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。 isPrototypeOf():判断当前对象是否为另一个对象的原型。 propertyIsEnumerable():判断某个属性是否可枚举。

Array

Array.isArray()判断是否是数组

sort

[10111, 1101, 111].sort(function (a, b) {
  return a - b;
})
// [111, 1101, 10111]

[
  { name: "张三", age: 30 },
  { name: "李四", age: 24 },
  { name: "王五", age: 28  }
].sort(function (o1, o2) {
  return o1.age - o2.age;
})
// [
//   { name: "李四", age: 24 },
//   { name: "王五", age: 28  },
//   { name: "张三", age: 30 }
// ]

some some方法是只要有一个数组成员的返回值是true,则整个some方法的返回值就是true,否则false。

var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
  return elem >= 3;
});
// true

every every方法则是所有数组成员的返回值都是true,才返回true,否则false。 注意,对于空数组,some方法返回false,every方法返回true,回调函数都不会执行

Number

属性:MAX_VALUE MIN_VALUE 方法:toFixed() toString() toExponential() // 科学计数表示 toPrecision() // 保留有效位数

自定义方法

Number.prototype.add = function (x) {
  return this + x;
};

String

match() match方法用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回null。

'cat, bat, sat, fat'.match('at') // ["at"]
'cat, bat, sat, fat'.match('xt') // null
var matches = 'cat, bat, sat, fat'.match('at');
matches.index // 1
matches.input // "cat, bat, sat, fat"

match方法还可以使用正则表达式作为参数

search() 等同于match,但是返回值为匹配的第一个位置。如果没有找到匹配,则返回-1。

replace() 用于替换匹配的子字符串,一般情况下只替换第一个匹配(除非使用带有g修饰符的正则表达式)。

'aaa'.replace('a', 'b') // "baa"

正则表达式

test esec

Promise

Promise对象只有三种状态。 异步操作“未完成”(pending) 异步操作“已完成”(resolved,又称fulfilled) 异步操作“失败”(rejected)

var promise = new Promise(function(resolve, reject) {
  // 异步操作的代码

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

promise应用-图片加载

var preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    var image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};

Ajax

function search(term, onload, onerror) {
  var xhr, results, url;
  url = 'http://example.com/search?q=' + term;

  xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);

  xhr.onload = function (e) {
    if (this.status === 200) {
      results = JSON.parse(this.responseText);
      onload(results);
    }
  };
  xhr.onerror = function (e) {
    onerror(e);
  };

  xhr.send();
}

search("Hello World", console.log, console.error);

or

function search(term) {
  var url = 'http://example.com/search?q=' + term;
  var xhr = new XMLHttpRequest();
  var result;

  var p = new Promise(function (resolve, reject) {
    xhr.open('GET', url, true);
    xhr.onload = function (e) {
      if (this.status === 200) {
        result = JSON.parse(this.responseText);
        resolve(result);
      }
    };
    xhr.onerror = function (e) {
      reject(e);
    };
    xhr.send();
  });

  return p;
}

search("Hello World").then(console.log, console.error);
var xhr = new XMLHttpRequest();

// 指定通信过程中状态改变时的回调函数
xhr.onreadystatechange = function(){
  // 通信成功时,状态值为4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

// open方式用于指定HTTP动词、请求的网址、是否异步
xhr.open('GET', '/endpoint', true);

// 发送HTTP请求
xhr.send(null);

跨域

CORS Q 请求 GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... 相应 Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

本地存储

localStorage sessionStorage 方法: getItem setItem removeItem clear()

遍历操作

for(var i = 0; i < localStorage.length; i++){
    console.log(localStorage.key(i));
}

storage事件

window.addEventListener("storage",onStorageChange);
// 回调函数接受一个event对象作为参数。这个event对象的key属性,保存发生变化的键名。
function onStorageChange(e) {
     console.log(e.key);    
}

除了key属性,event对象的属性还有三个: oldValue:更新前的值。如果该键为新增加,则这个属性为null。 newValue:更新后的值。如果该键被删除,则这个属性为null。 url:原始触发storage事件的那个网页的网址。

值得特别注意的是,该事件不在导致数据变化的当前页面触发。如果浏览器同时打开一个域名下面的多个页面,当其中的一个页面改变sessionStorage或localStorage的数据时,其他所有页面的storage事件会被触发,而原始页面并不触发storage事件。可以通过这种机制,实现多个窗口之间的通信。所有浏览器之中,只有IE浏览器除外,它会在所有页面触发storage事件。

IndexedDB

现有的浏览器端数据储存方案,都不适合储存大量数据:Cookie 不超过4KB,且每次请求都会发送回服务器端;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同)。 所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

indexedDB.open()打开数据库

var openRequest = indexedDB.open("test",1);

上面代码表示,打开一个名为test、版本为1的数据库。如果该数据库不存在,则会新建该数据库。如果省略第二个参数,则会自动创建版本为1的该数据库。

打开数据库的结果是,有可能触发4种事件。

success:打开成功。 error:打开失败。 upgradeneeded:第一次打开该数据库,或者数据库版本发生变化。 blocked:上一次的数据库连接还未关闭。

var openRequest = indexedDB.open("test",1);
var db;

openRequest.onupgradeneeded = function(e) {
    console.log("Upgrading...");
}

openRequest.onsuccess = function(e) {
    console.log("Success!");
    db = e.target.result;
}

openRequest.onerror = function(e) {
    console.log("Error");
    console.dir(e);
}

target.result属性就指向打开的IndexedDB数据库。

indexedDB实例对象的方法: 就是上面的DB

createObjectStore方法 用于创建存放数据的“对象仓库”(object store),类似于传统关系型数据库的表格。

db.createObjectStore("firstOS");

同源限制-跨文档通信

cookie localStorage indexedDB iframe DOM Ajax document.getElementById("myIFrame").contentWindow.document // 父窗口获取子窗口 不同源把偶偶 window.parent.document.body // 同理

对于完全不同源的网站,目前有两种方法,可以解决跨域窗口的通信问题。

片段识别符(fragment identifier)

http://example.com/x.html#fragment的#fragment // 父窗口可以把信息,写入子窗口的片段标识符。 var src = originURL + '#' + data; document.getElementById('myIFrame').src = src; // 子窗口通过监听hashchange事件得到通知。 window.onhashchange = checkMessage;

function checkMessage() { var message = window.location.hash; // ... } // 子对父 parent.location.href= target + “#” + hash;

跨文档通信API(Cross-document messaging) window.postMessage

// 父窗口aaa.com向子窗口bbb.com发消息
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

window.opener.postMessage('Nice to see you', 'http://aaa.com');

// 父窗口和子窗口都可以通过message事件,监听对方的消息。
<!-- event.source:发送消息的窗口
event.origin: 消息发向的网址
event.data: 消息内容 -->
window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

ajax跨域:

  1. JSONP
  2. Websocket origin:在白名单中

单线程模型

JavaScript同时只能执行一个任务,其他任务都必须在后面排队等待。

挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是JavaScript内部采用的Event Loop机制。

消息队列:

运行线程只要发现消息队列不为空,就会取出排在第一位的那个消息,执行它对应的回调函数。等到执行完,再取出排在第二位的消息,不断循环,直到消息队列变空为止。

进入消息队列的消息,必须有对应的回调函数。

另一种情况是setTimeout会在指定时间向消息队列添加一条消息。如果消息队列之中,此时没有其他消息,这条消息会立即得到处理;否则,这条消息会不得不等到其他消息处理完,才会得到处理。因此,setTimeout指定的执行时间,只是一个最早可能发生的时间,并不能保证一定会在那个时间发生。

一旦当前执行栈空了,消息队列就会取出排在第一位的那条消息,传入程序。程序开始执行对应的回调函数,等到执行完,再处理下一条消息。

Event Loop:

所谓Event Loop机制,指的是一种内部循环,用来一轮又一轮地处理消息队列之中的消息,即执行对应的回调函数。

下面是一些常见的JavaScript任务

同步任务指的是,在JavaScript执行进程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入JavaScript执行进程、而进入“任务队列”(task queue)的任务,只有“任务队列”通知主进程,某个异步任务可以执行了,该任务(采用回调函数的形式)才会进入JavaScript进程

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

运行以后的程序叫做“进程”(process)