https://wangdoc.com/javascript/

要点

  • 对象引用传递,其余值传递; 本质上还是值传递,按 c 里的指针
  • 获取长度时,可能不准确,因为 js 对 4 字符 unicode 支持不好,会被统计为 2 字符

对字符串长度判断要小心,需要专门的函数.

  • base64

base64 是为了防止字符串包含特殊字符而做的编码处理,不是加密.

btoa: 是 encoding!不是 base64 to ascii,而是 binary to ascii(即 base64 的编码为 base64).

atob: 是 decoding!…

  • 变量提升
f(); //allow 函数定义被提升
function f() {}

g(); // fault!
var g = function() {};
  • null, undefined {} ,’‘,NaN

null 和 undefined  很接近 Number(null) = 0, Number(undefined) = NaN

null,undefined,’‘,0,false 都是 false,{},[]空对象,空数组不是!

typeof null == 'object' // true
typeof undefined == 'undefined' // true
typeof 'undefined' == 'string' // true
null + 1 // 1
undefined + 1 // NaN
  • NaN NaN 为唯一不等于自身的值.
function myIsNaN(value) {
  return typeof value === "number" && isNaN(value);
}
  • 对象自身属性遍历
for (var key in person) {
  // in 遍历所有属性,包括继承
  if (person.hasOwnProperty(key)) {
    // own判断对象自身的属性
    console.log(key);
  }
}
  • 函数闭包

函数的作用域是它定义的地方,也就是诞生的地方.

闭包: 定义在一个函数内部的函数,闭包最大的特点,就是它可以“记住”诞生的环境.

var n = 1000;
function f1() {
  //f1 可以使用外部的n的值
  var n = 999;
  function f2() {
    // f2可以使用内部n的值,外部的n不行
    console.log(n);
  }
  return f2;
}
var f = f1(); //f1包裹了f2,和n f2是1个闭包.f2诞生在f1里,记住了f1的环境.

哪些非用不可的场景?哪些优雅的用法?

  • 函数定义后立即调用
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(factory);
  } else if (typeof exports === 'object') {
    module.exports = factory();
  } else {
    root.PhotoSwipe = factory();
}
})(this, function () {
    ....
}

作用:

1 是不必为函数命名,避免了污染全局变量;
2 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
  • 数组不能使用点结构访问
var arr = [1, 2, 3];
arr.0 // SyntaxError
arr[0] // Ok
  • 神奇的 array length

length 是个神奇的东西…js 确实也太随意了点.减少 lengh 会自动删除 array 的元素.

var arr = ["a", "b", "c"];
arr.length; // 3

arr.length = 2;
arr; // ["a", "b"]

arr.length = 0; //清空数组了..
arr.length = 10;
arr[5]; // 新增的空位为undefined

arr["p"] = "p";
arr.length; //1 给arr新增了属性,但不是元素,length不变!...

//delete命令删除一个数组成员,会形成空位,并且不会影响length属性
arr = [1, 2, 3];
delete arr[1];
arr.length; // 3

//空位为hole,要占据长度,不是undefined,但是其返回值确实是undefined..太变态了点?
arr = [1, , 3];
arr[1]; // undefined
//空位在遍历时,会跳过, undefined不会
arr.forEach(function(each) {
  console.log(each);
}); //1,3
arr = [1, undefined, 3];
arr.forEach(function(each) {
  console.log(each);
}); //1,undefined,h3
  • 数组遍历

遍历数组就老实的 for 好了, for..in 会遍历属性. 或者 forEach

// for循环
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
// forEach
  • 类数组对象与 arguments

如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object)。

为什么要讲它呢?它就是函数内部的参数,arguments这货..是数组又不是数组..好了,js 我服你.

典型的“类似数组的对象”是函数的 arguments 对象,以及大多数 DOM 元素集,还有字符串

//array-like object
var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

function cool(a,b,c) {
    var d,e,f=arguments[0],arguments[1],arguments[2];
    //这货还有个属性叫callee,也就是谁调用了它.
    arguments.callee== 'cool'; //true
}

//来,真男人,真数组,经常会看到这种用法,arguments转数组
var args = Array.prototype.slice.call(arguments);
args.shift(); //取第1个参数
  • Valueof 和 toString javascript 中所有数据类型都拥有 valueOf 和 toString 这两个方法,null 除外。它们俩解决 javascript 值运算与显示的问题.

当然对象可以定义自己的 toString,显示的更好

(function(a, b) {
  return "ab";
}.toString()); //返回"function(a,b){return "ab";}"
[1, 2, 3].toString(); //返回数组的值1,2,3

区别?

Array.isArray(arr.valueOf()); //True 是真的值
Array.isArray(arr.toString()); //False 仅仅是字符串
  • Object

Object: 所有对象之母 Object 本身也可是函数,将输入转成对象,如果输入已经是对象了就不转换了.用来判断是否是对象:

function isObject(value) {
  return value === Object(value);
}

var obj = new Object();
var obj = {}; //上面等价

Object 有自己的函数,叫静态方法,可以使用.

Object.keys([1, 2, 3]);

Object.prototype 上定义的方法,可以被 Ojbect 的实例使用.

[1, 2, 3].toString(); // 所有对象都有,"1,2,3"

var value = {
  1: 2,
  toString: function() {
    return "hehe";
  }
};
Object.prototype.toString.call(value); //对value这个对象调用原型的toString,即便toString被覆写,最有效的方式.

方法有:

Object.prototype.valueOf():返回当前对象对应的值。
Object.prototype.toString():返回当前对象对应的字符串形式。
Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式。
Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。
Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。
  • 通过 Object.prototype.toString 判断值的类型,代替 typeof
数值:返回[object Number]。
字符串:返回[object String]。
布尔值:返回[object Boolean]。
undefined:返回[object Undefined]。
null:返回[object Null]。
数组:返回[object Array]。
arguments 对象:返回[object Arguments]。
函数:返回[object Function]。
Error 对象:返回[object Error]。
Date 对象:返回[object Date]。
RegExp 对象:返回[object RegExp]。
其他对象:返回[object Object]。

typeof更准确的判断…那还要 typeof 干嘛..

var type = function(o) {
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

还记得这个吗…当然是有用的.

typeof null
"object"
typeof undefined
"undefined"

更叼的:

var type = function(o) {
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

[
  "Null",
  "Undefined",
  "Object",
  "Array",
  "String",
  "Number",
  "Boolean",
  "Function",
  "RegExp"
].forEach(function(t) {
  type["is" + t] = function(o) {
    return type(o) === t.toLowerCase();
  };
});
  • 属性由属性对象描述

返回:

Object.getOwnPropertyDescriptor([1,2,3],'1') //返回某个对象某个属性的属性对象

{value: 2, writable: true, enumerable: true, configurable: true}
configurable: true
enumerable: true
value: 2
writable: true
__proto__: Object

构造,比如给数组的新元素(属性),设置为不可写.

a = [1, 2, 3];
Object.defineProperty(a, "4", { value: 4, writable: false });
a[4]; //4
a[4] = 5; //5
a[4]; //4

enumerable 属性若为 false,一些语法就取不到该属性了.

for..in循环
Object.keys方法
JSON.stringify方法

但不管是否可遍历,可以使用 Object.getOwnPropertyNames 方法读到.

属性对象里还有 get/set. 即存取器,和 python 的 setter 是一样一样的:

var obj = Object.defineProperty({}, "p", {
  get: function() {
    return "getter";
  },
  set: function(value) {
    console.log("setter: " + value);
  }
});

obj.p; // "getter"
obj.p = 123; // "setter: 123"

//更简洁的做法:
var obj = {
  get p() {
    return "getter";
  },
  set p(value) {
    console.log("setter: " + value);
  }
};
  • 冻结方法 Oject.freeze

Object.freeze(valuel)可冻结某个对象,不让改,加,删它的属性. 但是可在原型上加,原型也冻掉,有点扯.. 另外如果属性是数组,对象之类的,是可以改变其元素的.

a={1:[1,2,3]}
{1: Array(3)}
Object.freeze(a)
a[1]=2
a //[1,2,3] //freeze后,无法修改

a[1][2]=4 //直接修改数组1的元素,ok
a // [1,2,4]

Array

  • sort 方法 sort 不是想象中的 sort,得先转成字符串!
[101, 11]
  .sort() // 101,11 没变化, 因为字符101的`0`在11的`1`之前
  [(101, 11)].sort(function(a, b) {
    return a - b;
  }); // 11 101
  • map
[1,2,3].map(function(a){return a*a;}) //1,4,9
//map方法的第二个参数,将回调函数内部的this对象
var arr=[6,6,6];
[1,2,3].map(function(a){return a*a;},arr) //36,36,36
//map方法不会跳过undefined和null,但是会跳过空位。
[1,,3].map(function(a){return 'a';}) // 'a','a'
[1,null,3].map(function(a){return 'a';}) // 'a','a','a'

forEach 类似 map,但是只处理,不返回值

  • map-reduce
[1, 2, 3]
  .map(a => {
    return a * a;
  })
  .reduce((a, b) => {
    return a + b;
  });
  • Array 链式使用
var users = [
  { name: "tom", email: "tom@example.com" },
  { name: "peter", email: "peter@example.com" }
];

//比forEach直接处理 还是要优雅多了,这也是map forEach filter的精髓.
users
  .map(function(user) {
    return user.email;
  })
  .filter(function(email) {
    return /^t/.test(email);
  })
  .forEach(function(email) {
    console.log(email);
  });
// "tom@example.com"

包装对象

三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。即 Number,String,and Boolean.

var n = new Number(123);
typeof n; //"object" new出来的仅仅是object
var m = Number(123);
typeof m; //"number" //强制转换的是包装对象