Web 阮神js学习笔记_基础
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" //强制转换的是包装对象