// 基本数据类型
var tmp1 = "字符串";
var tmp2 = 1;
var tmp3 = 1.1;
var tmp4 = true;
// 特殊类型
var tmp5= undefined;
var tmp6 = null; // null 是一个 object
// 对象
var tmp7 = {}; // 对象
var tmp8 = []; // 数组
var tmp9 = function(){}; // 函数
console.log("typeof(tmp1)", typeof (tmp1));
console.log("typeof(tmp2)", typeof (tmp2));
console.log("typeof(tmp3)", typeof (tmp3));
console.log("typeof(tmp4)", typeof (tmp4));
console.log("typeof(tmp5)", typeof (tmp5));
console.log("typeof(tmp6)", typeof (tmp6));
console.log("typeof(tmp7)", typeof (tmp7));
console.log("typeof(tmp8)", typeof (tmp8));
console.log("typeof(tmp9)", typeof (tmp9));
// function 因为有类object 的操作, 所以也属于 object
console.log("tmp9.name", tmp9.name); // 获取函数名
//typeof(tmp1) string
//typeof(tmp2) number
//typeof(tmp3) number
//typeof(tmp4) boolean
//typeof(tmp5) undefined
//typeof(tmp6) object
//typeof(tmp7) object
//typeof(tmp8) object
//typeof(tmp9) function
//tmp9.name tmp9
null 和 undefined 的区别
boolean
布尔值表示真和假,true 表示真,false 表示假;
下列运算符会返回布尔值:
表示 false 的值
在自动数据转换中,下列值会表示 false:
其他的值都会被当成 true;
空数组 [] 和空对象 {} 对应的布尔值都是 true;
在 js 中,所有的数值都是 64 位浮点数的形式进行存储的,也就是说在 js 底层,没有整数只有浮点数;
因为浮点数精度的问题,js 在进行浮点数运算的时候经常会出现问题:
console.log(0.1 + 0.2);
// 0.30000000000000004
默认情况下,js 内部会将八进制,十六进制,二进制转为十进制;
NaN 是 js 中的特殊值,表示非数字 (Not a Number), 主要出现在字符串解析成数字出错的时候;
Infinity 用来表示无穷,一般出现在两种场景下:
parseInt(string[,radix])
parseInt(string[,radix])将字符串解析成数值;如果入参非字符串,则会调用入参的toString方法转换为字符串再进行转换;如果设置了第二个参数 radix, 则会将字符串按指定的 radix 进制转换成十进制;返回数值或者 NaN
var a = '0xf';
console.log(a, 16); // 将 16 进制的 0xf 转为十进制
// 15
parseFloat(string)
parseFloat(string)将字符串入参解析成浮点数;返回浮点数或者 NaN ;
var a = "4.567";
console.log(parseFloat(a)); // 4.567
// 当入参有非法字符, 则只保留合法部分进行转换
var b = "4.567abcd";
console.log(parseFloat(b)); // 4.567
// 第二个小数点忽略
var c = "1.2.3";
console.log(parseFloat(c)); // 1.2
// 起始为非法字符则直接返回 NaN
var d = "aaa1.2"; // NaN
console.log(parseFloat(d));
isNaN()
判断某个入参是否是 NaN; 可以利用 NaN 的不等性来进行判断
var a = NaN;
if (a != a ){
console.log("it is NaN");
}
isFinite()
判断某个入参是否是 Infinity;
js 中使用的字符集为 Unicode 字符集,所有的字符串都使用 Unicode 表示;
var f\u006F\u006F = 'abc';
console.log(f\u006F\u006F);
Obejct.keys()可以查看该对象自身的属性,继承来的属性无法查看
var a = {
hello: function(){
consoel.log("hi");
},
table: [1, 2, 3],
name: "kevin",
age: 21,
married: false
}
console.log(Object.keys(a));
//[ 'hello', 'table', 'name', 'age', 'married' ]
var a = {
hello: function(){
consoel.log("hi");
},
table: [1, 2, 3],
name: "kevin",
age: 21,
married: false
}
delete a.age, delete a.hello;
console.log(Object.keys(a));
// [ 'table', 'name', 'married' ]
JSvar a = {
hello: function () {
consoel.log("hi");
},
table: [1, 2, 3],
name: "kevin",
age: 21,
married: false
}
console.log(a.hasOwnProperty('table'));
if ("table" in a) {
console.log("table is property of a");
}
var a = {
hello: function(){
consoel.log("hi");
},
table: [1, 2, 3],
name: "kevin",
age: 21,
married: false
}
for (const aKey in a) {
// 使用 hasOwnProperty 进行筛选
if (a.hasOwnProperty(aKey)) {
console.log(aKey);
}
}
js 中函数声明有三种方式
js 将函数看成是一个值,与其他数据类型一样,凡是可以使用其他数据类型的地方都可以使用函数,例如:
js 中全局变量名存在变量提升,函数内部的局部变量也存在变量提升;
function outer() {
console.log(a); // undefined 说明全局变量存在变量提升
console.log(b); // undefined 说明局部变量存在变量提升
var b = 2;
}
outer();
var a = 1;
js 中函数的声明也存在变量提升,可以先调用该方法,再定义该方法
b();
function b() {
console.log("b called");
}
js 中函数的参数不是必须的,允许省略函数的参数;
函数的 length 属性只和函数声明时形参的个数有关,和实际调用时传入的参数个数无关;
var a = 1;
var b = 2;
function f() {
var b = 3;
console.log(a, b);
function f1() {
var b = 4;
console.log(a, b);
}
f1();
}
f(); // 1 3// 1 4
链式作用域查找
子级会优先使用自己的作用域,如果变量存在则使用,不存在则会依次向上寻找,直至全局作用域;
闭包定义
通过闭包实现简单的计数器
function count() {
var count = 0;
function f() {
count++;
console.log("count", count);
}
return f;
}
f = count();
f();
f();
f();
js 中有三种立即调用函数的方式
通常情况下,只对匿名函数使用这种立即执行的表达式,这样有两个目的:
eval 别名调用
eval 的别名调用在 nodejs 下无法跑通,需要在浏览器下运行;
需要注意,在 eval 通过别名调用的时候,作用域永远是全局作用域;
var a = 1;
var e = eval;
(function () {
var a = 2;
e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}())
var a = 1;
var e = eval;
(function () {
var a = 2;
e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}
())
var a = [1, 1.1, true, {}, [], "hello", null, undefined];
console.log("a.length", a.length);
a.name = "add a.name property";
for (const aKey in a) {
console.log(aKey, a[aKey]);
}
console.log("Object.keys(a)", Object.keys(a));
a.length = 0;
console.log("a", a);
console.log("a['name']", a['name']);
console.log("a.name", a.name);
console.log("a[0]", a[0]);
var a = 1;
var e = eval;
(function () {
var a = 2;
e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
}
())
var a = [1, 1.1, true, {}, [], "hello", null, undefined];
console.log("a.length", a.length);
a.name = "add a.name property";
for (const aKey in a) {
console.log(aKey, a[aKey]);
}
console.log("Object.keys(a)", Object.keys(a));
a.length = 0;
console.log("a", a);
console.log("a['name']", a['name']);
console.log("a.name", a.name);
console.log("a[0]", a[0]);
var a = [1, 1.1, true, {}, [], "hello", null, undefined];
for infor(const aKey in a) {
console.log("aKey:", aKey, "value:", a[aKey]);
}
console.log("-------------------------------")
forfor(var i = 0; i <= a.length; i++) {
console.log("index:", i, "value:", a[i]);
}
console.log("-------------------------------")
whilevar index = 0;
while (index <= a.length) {
console.log("index:", index, "value:", a[index]);
index++;
}
console.log("-------------------------------")
forEacha.forEach(function (value, key) {
console.log("key:", key, "value:", value);
})
js 中的数组支持空值,出现空值时会占用该索引位,但是遍历的时候不会遍历该索引的值
var a = [1, 2, 3, , 5];
a.forEach(function (value, key) {
console.log("key", key, "value", value);
})
// key 0 value 1
// key 1 value 2
// key 2 value 3
// key 4 value 5
当+加号作为操作符,且操作数中含有字符串时,会自动将另一个操作数转为字符串;
规则如下:
// 基础类型
// 自动数据类型转换
// + 字符串
// 1. 字符串 + 基础数据类型: 会直接将基础数据类型转为和字面量相同的字符串
var tmp1 = "" + 3;
console.log("tmp1", tmp1); // tmp1 "3"
var tmp2 = "" + true;
console.log("tmp2", tmp2); // tmp2 "true"
var tmp3 = "" + undefined;
console.log("tmp3", tmp3); // tmp3 "undefined"
var tmp4 = "" + null;
console.log("tmp4", tmp4); // tmp4 "null"
// 字符串+复合数据类型: 复合数据类型会先调用 valueOf 方法, 如果此方法返回的是引用类型, 则再调用 toString()方法, 最后将返回值转为字符串类型
var tmp5 = [1, 2, 3] + "";
console.log("tmp5", tmp5); // tmp5 1,2,3
var tmp6 = {} + "";
console.log("tmp6", tmp6); // tmp6 [object Object]
// 重写 toString 方法
var o = {
toString: function () {
return 1;
}
}
var tmp7 = o + "";
console.log("tmp7", tmp7) // tmp7 "1"
// 重写 valueOf 方法
o.valueOf = function () {
return 2;
}
var tmp8 = "" + o;
console.log("tmp8", tmp8); // tmp8 2
var a = {
valueOf: function () {
return {}
},
toString: function () {
return "toString"
}
};
console.log("" + a); // toString
数值转布尔值
数值在逻辑判断条件下会自动转成布尔值;±0 和 NaN 为 false, 其他数值都是 true;
if (0) {
console.log("0 is true");
}else{
console.log("0 is false");
}
if (NaN) {
console.log("NaN is true");
}else{
console.log("NaN is false");
}
if (-0) {
console.log("-0 is true");
} else {
console.log("-0 is false")
}
// 0 is false
// NaN is false
// -0 is false
字符串转布尔值
空字符串”” 或者’’为 false, 其他都是 true
undefined 和 null 转布尔值
undefined 和 null 转为布尔值都是 false
对象转布尔值
只有对象为 null 或者 undefined 时,转为布尔值才是 false; 其他情况下 (包括空对象 {} 和空数组 []) 转为布尔值都是 true;
var o = {};
if(o){
console.log("{} is true");
}else{
console.log("[] is true");
}
// {} is true ; 空数组[] 同理
一元操作符+和-都会触发其他类型转为数值;
数学运算符操作的两个操作数都不是字符串时,也会触发其他类型转为数值的操作;
转化规则如下:
Error 对象通常包含常用的三个属性,且必须包含 message 属性;
var e = new Error("自定义异常触发");
e.name = "自定义异常名称";
console.log(e.name);
console.log(e.message);
console.log(e.stack);
/*
自定义异常名称
自定义异常触发
自定义异常名称: 自定义异常触发
at Object.<anonymous> (/Users/zhangyang/codes/antiJs/js_learn/_03_基本数据类型(下)/tmp.js:201:9)
at Module._compile (internal/modules/cjs/loader.js:1085:14)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
at Module.load (internal/modules/cjs/loader.js:950:32)
at Function.Module._load (internal/modules/cjs/loader.js:790:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
at internal/main/run_main_module.js:17:47
*/
手动抛出异常并捕获
try {
var e = new Error("message 自定义异常");
e.name = "自定义异常名称";
throw e;
} catch (e) {
console.log("e.name", e.name, "e.message", e.message);
console.log("e.stack", e.stack);
}
throw
throw语句用来抛出用户定义的异常,当前函数的执行将被停止,throw之后的代码不会被执行;并且代码将会进入调用栈中第一个catch块中;如果没有catch块,程序将会终止;
try {
console.log("before throw error");
throw new Error("throw error");
console.log("after throw error");
} catch (e) {
console.log("catch error", e.message);
}
// before throw error
// catch error throw error
try/catch/finally
try 的三种声明形式:
try/catch 主要用于捕获异常,try/catch 语句包含一个 try 块,和至少一个 catch 块或者一个 finally 块;
try {
try {
throw new Error("error1");
} finally {
throw new Error("error2");
}
} catch (e) {
console.log("e.message", e.message);
}
// e.message error2
function test() {
try {
throw new Error("error1");
return 1
}catch (e) {
throw new Error("error2");
return 2;
}finally {
return 3;
}
}
console.log("test()", test());
// test() 3
所谓静态方法,就是设置在 Object 类上的方法;
function Test(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
};
var test = new Test(1, 2, 3);
Object.defineProperty(test,"d",{
configurable: false, // 是否可以删除 默认 false 不可删除
enumerable: false, // 是否是可迭代字段
value: "4",
writable: true,
// 使用Object.defineProperty() 定义对象属性时,如已设置 set 或 get, 就不能设置 writable 和 value 中的任何一个了
// get: function(){
// return this.d;
// },
// set: function (value) {
// this.d = value;
// return this.d;
// }
})
// 遍历对象的属性, 返回可枚举的属性名, 仅可查看变量本身的属性, 不能查看继承来的属性
console.log(Object.keys(test)); // [ 'a', 'b', 'c' ]
// 遍历对象的属性, 可以返回不可枚举的类型
console.log(Object.getOwnPropertyNames(test)); // [ 'a', 'b', 'c', 'd' ]
// 获取对象的所有属性的描述对象
console.log(Object.getOwnPropertyDescriptors(test));
/*
* {
a: { value: 1, writable: true, enumerable: true, configurable: true },
b: { value: 2, writable: true, enumerable: true, configurable: true },
c: { value: 3, writable: true, enumerable: true, configurable: true },
d: {
value: '4',
writable: true,
enumerable: false,
configurable: false
}
}
*/
// 设置对象的原型
Object.setPrototypeOf(test, Test);
// 获取对象的原型对象, 可以用于原型检测
console.log(Object.getPrototypeOf(test));
var init = function (a, b, c) {
this.a = a;
this.b = b;
this.c = c;
};
var initObj = new init(1, 2, 3);
console.log("initObj", initObj);
这三种创建对象方式的区别:
var init = function (a, b, c) {
this.a = a;
this.b = b;
this.c = c;
};
var obj = new init(1, 2, 3);
var obj2 = Object.create(obj, {
"p": {
"value": "p1"
}
})
console.log("obj2", obj2); // obj2 init {}
console.log("obj2.p", obj2.p); // obj2.p p1
prototype 和proto区别
function test(){
console.log("test");
}
console.log(test.prototype.__proto__.__proto__); // null
console.log(test.__proto__.__proto__.__proto__); // null
JS
function DTA(boss, employee) {
this.boss = boss;
this.employee = employee;
}
DTA.prototype.room = {
"roomNum": 1
}
DTA.prototype.name = "kevin";
DTA.prototype.dowork = function () {
return this.boss +" " + this.employee +" "+ "do work now";
}
var dta = new DTA("kevin", "rub");
var dta2 = new DTA("kevin2", "rub2");
// 修改 DTA.prototype 原型上的属性, 会影响到所有的实例
DTA.prototype.name = "change";
console.log(dta2.__proto__ === DTA.prototype); // true
console.log(dta2.name); // change
dta2.room.roomNum = 2; // 从其他对象上修改原型对象的属性, 也会影响其他对象
dta2.__proto__.name = "change2";
console.log(dta.name); // change2
console.log("dta.boss", dta.boss);
console.log("dta.employee", dta.employee);
console.log("dta.dowork()", dta.dowork());
console.log("dta.room.roomNum", dta.room.roomNum); // 2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iG8FuHJ6-1666175308667)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b4f04d91-7ee1-4312-bb6a-25ded0ca352b/Untitled.png)]
// 函数的 .prototype.constructor 等于函数本身
function test() {};
console.log(test.prototype.constructor === test);
// 环境监测 document.constructor === HTMLDocument
function HTMLDocument() {};
var document = new HTMLDocument();
console.log(document.constructor === HTMLDocument);
var document2 = new document.__proto__.constructor();
console.log(document2.constructor === HTMLDocument);
// 环境监测 navigator.constructor === Navigator
function Navigator() {};
var navigator = new Navigator();
console.log(navigator.constructor === Navigator);
var navigator2 = new navigator.constructor();
console.log(navigator2.constructor === Navigator); // 不带__proto__也可以, 默认会自动去原型链上查找
// 环境监测 配合 toString()进行检测,node 下不会报错, 更难发现
// navigator.constructor.toString() === "function Navigator() { [native code] }"
function Navigator() {}
Navigator.toString = function () { // Navigator.prototype.constructor.toString
return "function Navigator() { [native code] }"
}
var navigator = new Navigator();
console.log(navigator.constructor.toString());
console.log(navigator.constructor.toString() === "function Navigator() { [native code] }");
简易版 hook cookie
var cookie = document.cookie;
Object.defineProperty(document, 'cookie', {
get: function(){
console.log("getter:" + cookie);
return cookie;
},
set: function(value){
console.log("setter:" + cookie);
if (value.indexOf("targetCookie") > -1){
debugger;
}
cookie = value;
}
})
油猴 hook cookie
// ==UserScript==
// @name Hook Cookie
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @include *
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
console.log("hook cookies start ...");
var cookie_cache = document.cookie;
Object.defineProperty(document, 'cookie', {
get: function () {
console.log("get cookie:" + cookie_cache);
return cookie_cache;
},
set: function (val) {
console.log('Setting cookie', val);
// 填写cookie名
if (val.indexOf('w_token') != -1) {
debugger;
}
var cookie = val.split(";")[0];
var ncookie = cookie.split("=");
var flag = false;
var cache = cookie_cache.split("; ");
cache = cache.map(function (a) {
if (a.split("=")[0] === ncookie[0]) {
flag = true;
return cookie;
}
return a;
})
cookie_cache = cache.join("; ");
if (!flag) {
cookie_cache += cookie + "; ";
}
return cookie_cache;
}
});
})();
对象拷贝需要做到两件事:
function copyObj(obj){
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
}
function kevin(a){
this.a = a;
}
var kevinObj = new kevin(1);
var kevinObj2 = copyObj(kevinObj);
三种原始类型的值在一定条件下也会自动转为对象,Boolean (), String () 和 Number ();
当使用 new 关键字的时候就会创建一个新的包装对象,当不使用 new 关键字时就是强制类型转换;
创建一个新的 Date 对象的唯一方式是通过 new 操作符;
new Date();
new Date(value);
new Date(dateString);
new Date(year, monthIndex[, day[, hours[, minutes[, seconds[, milliseconds]]]]]);
var d = new Date();
console.log(d.getTime());
var getTime = d.getTime;
getTime(); // 直接调用 error, this is not a Date objectvar
d_getTime = getTime.bind(d, []);
console.log(d_getTime());
var d = new Date();
var getTime = d.getTime;
var result = getTime.apply(d, []);
console.log(result);
var d = new Date();
var getTime = d.getTime;
var result = getTime.call(d);
console.log(result);
es6 是 js 语言的下一代标准,在 2015 年 6 月正式发布;它的目标是让 js 语言可以用来编写复杂的大型应用程序;es 和 js 的关系是: es 是 js 的规格,js 是 es 的实现;
es6 中提出了两个新的变量声明命令: let 和 const; let 可以完全取代 var; var 命令存在变量提升的效果,let 命令则不存在这个问题;所以尽量使用 let, 减少 var 的使用;
在全局环境中使用 var 声明变量,会直接将变量定义到全局环境中,在全局环境中使用 let 和 const 是不会直接将变量定义到全局环境上的;
// 在浏览器环境下运行
var a = 10;
let b = 20;
const c = 30;
console.log(window.a); // 10
console.log(window.b); // undefined
console.log(window.c); // undefined
在 es5 中,只有函数作用域和全局作用域;在 es6 中新增了块级作用域;比如典型的 for 循环;使用 var 会导致变量泄漏到全局环境中,使用 let 就不会存在该问题;
for (var i = 0; i < 10; i++){
console.log(i);
}
console.log(i);
for (let a = 0; a < 10; a++){
console.log(a);
}
console.log(a); // undefined
// es5
// var team = {company: "dta"}
// es6
const company = "dta"; // 先定义属性
const team = {company}; // 再定义对象
console.log(team);
// es6
let boss = "Dta boss";
let Dta = {
[boss]: "roysue"
}
console.log("Dta['Dta boss']", Dta['Dta boss']);
// es5var
team = {
dowork: function () {
return "work!";
}
}
// es6var
team = {
dowork() {
return "work";
}
}
在 es6 中新增了动态的字符串模板,使用反引号表示,使用 ${} 进行填充;
var name = "kevin";
console.log(`hi, ${name}`);
数组解构
const arr = [1,2,3,4];
const [first, second] = arr; // first = arr[0], second = arr[1];
console.log(first, second); // 1,2
对象解构
var obj = {
firstName: "chen",
lastName: "guilin"
}
function getFullName(obj){
const {firstName, lastName} = obj;
console.log(firstName, lastName);
}
getFullName(obj); // chen guilin
快速复制对象
let Dta = {
name: "kevin",
age: 29,
dowork(){
return "work";
}
}
let newDta = {...Dta}; // 用于取出参数对象的所有可遍历属性, 拷贝到当前对象中
console.log("newDta", newDta);
// 其实是调用了 Object.assign()方法
let roysue = {};
let r1ysue = Object.assign({}, roysue);
// 等同于
let r2ysue = {...roysue};
// 其他快速克隆对象的方法
var obj = {};
const clone1 = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
const clone2 = Object.assign(
Object.create(Object.getPrototypeOf(obj),
obj
);
const clone3 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
数组创建对象
function processInput(){
let [a,b,c,d] = [1,2,3,4];
return {a,b,c,d}
}
console.log(processInput()); // {a:1, b:2, c:3, d:4};
立即执行函数可以写成箭头函数的形式:
(() => {
console.log("welcome to the DTA");
})();
在那些使用匿名函数当做参数的场景下,尽量使用箭头函数作为替代,因为使用箭头函数更加简洁,而且箭头函数在定义函数的时候就绑定了 this; 不需要在执行的时候考虑 this 的问题;
let a = [1, 2, 3].map((x) => {
return x * x;
});
console.log("a", a); // [1,4,9]
function Test(){
this.s1 = 0;
this.s2 = 0;
setInterval(()=>{this.s1++;}, 1000);
setInterval(function () {
this.s2++;
}, 1000);
}
var test = new Test();
setInterval(() => {
console.log(test.s1);
}, 3000); // 2 5 ...
setInterval(() => {
console.log(test.s2);
}, 3000); // 0 0 ...
// commonJs 模块引入 node 还是使用这个
let {stat, exists, readfile} = require("fs");
// 等同于
let _fs = require("fs");
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
// es6 模块
import {stat, exists, readfile} from 'fs';
在 js 中引入其他文件中的函数或者对象
// 当前目录下新建 testImport.js 文件
var dta = {
name: "dta",
version : '1.0.0',
boss: "roysue",
dowork: function (name) {
console.log(`${this.name} is working`);
}
}
module.exports = dta; // 将当前对象导出
// 在其他文件中引入
let dta = require("./testImport");
console.log(dta);
dta.dowork();
// 在当前目录下新建 testImport.js 文件
function add(x,y){
return x+y;
}
exports.add = add;
// 在其他文件中引入
let {add} = require("./testImport");
var result = add(1,2);
console.log("result",result);
如果读取对象内部的某个属性,往往需要判断一下该对象是否存在这属性;比如message.body.user.firstName, 安全的写法为:
const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default';
这样写相对比较麻烦,在 es6 中可以使用链式判断运算符进行判断;
let Dta = {
name: "kevin",
age: 29,
dowork(){
return "work";
},
room: {
r1: {
l: "l1"
}
}
}
console.log(Dta.room.r1.l); // l1
console.log(Dta?.room?.r1?.l?.f || "default"); // default
在读取对象属性的时候,如果该属性是 null 或者 undefined, 可以通过 || 设置默认值const roysue = Dta.Boss.roysue || 'roysue'但是这样写的话在,当Dta.Boss.roysue的值为空字符串或者 false 或者 0 的时候也会触发默认值;
为了避免这个错误,es6 中新增了 null 判断运算符??;只有当运算符的左侧值为 null 或者 undefined 时,才会返回右侧的默认值
const roysue = Dta.Boss.KK ?? 'KK';
在 es5 中,类的表示通常是:
function bb(boss, employee) {
this.boss = boss;
this.employee = employee;
}
Dta.prototype.toString = function () {
return '(' + this.boss + ',' + this.employee + ')';
}
var bb = new bb('kevin', 'rub');
console.log(dta.toString());
在 es6 中,为了让类的概念更接近 java 和 c++ 的类,新增了 class 关键字;
class bb{
constructor(boss, employee) {
this.boss = boss;
this.employee = employee;
}
toString(){
return '(' + this.boss + ',' + this.employee + ')';
}
}
var bb = new bb("kevin", "rub");
console.log(bb.toString());
代理器案例
// Proxy 和 Reflect
let obj = new Proxy({}, {
get(target, p, receiver) {
console.log(`get ${p}`);
return Reflect.get(target, p, receiver);
},
set(target, p, value, receiver) {
console.log(`set key: ${p}, value: ${value}`);
return Reflect.set(target, p, value, receiver);
}
});
obj.name = "kevin";
obj.name;
Proxy 作为其他对象的原型
Proxy 实例也可以作为其他对象的原型对象,可以用来拦截原型链的访问:
var proxy = new Proxy({}, {
get: function(target, propertyKey, receiver){
return 35;
}
})
var obj = Object.create(proxy);
console.log(obj.time); // 35
Proxy 拦截函数调用
// Proxy 拦截函数调用
function test(a, b) {
return a + b;
}
test = new Proxy(test, {
apply(target, thisArg, argArray) {
console.log("thisArg", thisArg);
console.log("target", target);
console.log("argArray", argArray);
let result = Reflect.apply(target, thisArg, argArray);
console.log("result", result);
return result;
}
})
test(1, 2);
/*
thisArg undefined
target [Function: test]
argArray [ 1, 2 ]
result 3
* */
Proxy 监控构造函数的调用
// Proxy 监控构造函数
function Test(a,b) {
this.a = a;
this.b = b;
return a + b;
}
Test = new Proxy(Test, {
construct(target, argArray, newTarget) {
console.log("target", target);
console.log("argArray", argArray);
console.log("newTarget", newTarget);
var result = Reflect.construct(target, argArray, newTarget);
console.log("result", JSON.stringify(result));
return result
}
})
let test = new Test(1, 2);
console.log("test", test);
/*
target [Function: Test]
argArray [ 1, 2 ]
newTarget [Function: Test]
result {"a":1,"b":2}
test Test { a: 1, b: 2 }
*/
采用 Proxy 帮助补充环境
let mywindow = {};
let mynavigator = {};
let rawstringify = JSON.stringify;
JSON.stringify = function (Object){
if((Object?.value ?? Object) === global){
return "global"
}else{
return rawstringify(Object)
}
}
function checkproxy(){
//Object.keys(window)
window.a = {
"b":{
"c":{
"d":123
}
}
}
window.a.b.c.d = 456
window.a.b
window.btoa("123")
window.atob.name
"c" in window.a
delete window.a.b
Object.defineProperty(window, "b",{
value:"bbb"
})
Object.getOwnPropertyDescriptor(window,"b")
Object.getPrototypeOf(window)
Object.setPrototypeOf(window,{"dta":"dta"})
// for (let windowKey in window) {
// windowKey
// }
Object.preventExtensions(window)
Object.isExtensible(window)
}
function getMethodHandler(WatchName){
let methodhandler = {
apply(target, thisArg, argArray) {
let result = Reflect.apply(target, thisArg, argArray)
console.log(`[${WatchName}] apply function name is [${target.name}], argArray is [${argArray}], result is [${result}].`)
return result
},
construct(target, argArray, newTarget) {
var result = Reflect.construct(target, argArray, newTarget)
console.log(`[${WatchName}] construct function name is [${target.name}], argArray is [${argArray}], result is [${JSON.stringify(result)}].`)
return result;
}
}
return methodhandler
}
function getObjhandler(WatchName){
let handler = {
get(target, propKey, receiver) {
let result = Reflect.get(target, propKey, receiver)
if (result instanceof Object){
if (typeof result === "function"){
console.log(`[${WatchName}] getting propKey is [${propKey}] , it is function`)
//return new Proxy(result,getMethodHandler(WatchName))
}else{
console.log(`[${WatchName}] getting propKey is [${propKey}], result is [${JSON.stringify(result)}]`);
}
return new Proxy(result,getObjhandler(`${WatchName}.${propKey}`))
}
console.log(`[${WatchName}] getting propKey is [${propKey}], result is [${result}]`);
return result;
},
set(target, propKey, value, receiver) {
if(value instanceof Object){
console.log(`[${WatchName}] setting propKey is [${propKey}], value is [${JSON.stringify(value)}]`);
}else{
console.log(`[${WatchName}] setting propKey is [${propKey}], value is [${value}]`);
}
return Reflect.set(target, propKey, value, receiver);
},
has(target, propKey){
var result = Reflect.has(target, propKey);
console.log(`[${WatchName}] has propKey [${propKey}], result is [${result}]`)
return result;
},
deleteProperty(target, propKey){
var result = Reflect.deleteProperty(target, propKey);
console.log(`[${WatchName}] delete propKey [${propKey}], result is [${result}]`)
return result;
},
getOwnPropertyDescriptor(target, propKey){
var result = Reflect.getOwnPropertyDescriptor(target, propKey);
console.log(`[${WatchName}] getOwnPropertyDescriptor propKey [${propKey}] result is [${JSON.stringify(result)}]`)
return result;
},
defineProperty(target, propKey, attributes){
var result = Reflect.defineProperty(target, propKey, attributes);
console.log(`[${WatchName}] defineProperty propKey [${propKey}] attributes is [${JSON.stringify(attributes)}], result is [${result}]`)
return result
},
getPrototypeOf(target){
var result = Reflect.getPrototypeOf(target)
console.log(`[${WatchName}] getPrototypeOf result is [${JSON.stringify(result)}]`)
return result;
},
setPrototypeOf(target, proto){
console.log(`[${WatchName}] setPrototypeOf proto is [${JSON.stringify(proto)}]`)
return Reflect.setPrototypeOf(target, proto);
},
preventExtensions(target){
console.log(`[${WatchName}] preventExtensions`)
return Reflect.preventExtensions(target);
},
isExtensible(target){
var result = Reflect.isExtensible(target)
console.log(`[${WatchName}] isExtensible, result is [${result}]`)
return result;
},
ownKeys(target){
var result = Reflect.ownKeys(target)
console.log(`[${WatchName}] invoke ownkeys, result is [${JSON.stringify(result)}]`)
return result
},
apply(target, thisArg, argArray) {
let result = Reflect.apply(target, thisArg, argArray)
console.log(`[${WatchName}] apply function name is [${target.name}], argArray is [${argArray}], result is [${result}].`)
return result
},
construct(target, argArray, newTarget) {
var result = Reflect.construct(target, argArray, newTarget)
console.log(`[${WatchName}] construct function name is [${target.name}], argArray is [${argArray}], result is [${JSON.stringify(result)}].`)
return result;
}
}
return handler;
}
const window = new Proxy(Object.assign(global,mywindow), getObjhandler("window"));
const navigator = new Proxy(Object.create(mynavigator), getObjhandler("navigator"));
checkproxy()
module.exports = {
window,
navigator
}
Ajax 操作可以被当做同步任务处理,也可以当做异步任务处理,由参数 async 控制;如果是同步任务,主线程就会一直等待 Ajax 操作返回结果,才会继续执行后面的代码;如果是异步任务,那么主线程发出 Ajax 请求后会直接向下执行其他代码,等到 Ajax 操作有了结果,引擎会其对应的回调函数放入主线程进行处理;
定时器是 js 中提供定时执行代码的功能;主要由setTimeout()和setInterval()这两个函数来完成;它们可以向任务队列中添加定时任务;
运行机制
setTimeout
// setTimeout()
var x = "from window";
var obj = {
x: "from obj",
y: function () {
console.log(this.x);
}
}
setTimeout(obj.y, 1000); // node 环境下输出 undefined, 浏览器下输出 from window
// 将该方法放在一个函数中, 防止 this 指向全局环境
setTimeout(function () {
obj.y(); // node 和浏览器下都输出 from obj;
}, 1000);
setTimeout(obj.y.bind(obj), 1000); // node 和 浏览器下都输入 from obj
setTimeout 也可以实现指定间隔时间执行,相当于下面的 setInterval 方法;区别在于 setTimeout 会考虑自身的运行时间,可以保证严格的间隔时间;
var i = 1;
function f() {
console.log(i);
i++;
setTimeout(f, 1000);
}
setTimeout(f, 1000);
setInterval
var i = "from global";
var obj = {
a : 1,
i: "from obj",
f: function () {
console.log("this.i", this.i);
}
}
setInterval(obj.f, 1000); // node 下输出为 undefined, 浏览器下输出为 from global
setInterval(function () {
obj.f();
}, 1000) // from obj
setInterval(obj.f.bind(obj), 1000); // from obj
清除定时器
// 清除所有定时器
(function(){
var gid = setInterval(clearAllTimeouts,0);
console.log(gid);
function clearAllTimeouts(){
var id = setTimeout(function(){},0);
while (id > 0){
if (id !== gid){
clearTimeout(id);
}
id--;
}
}
})()
var objPromise = function (input) {
return new Promise(function (resolve, reject) {
var obj = {};
obj.name = input;
setTimeout(resolve, 1000, obj.name);
console.log("继续执行");
});
}
function resolve(name) {
console.log("name", name);
return name;
}
function resolve2(name) {
console.log("name2", name);
return name;
}
objPromise("kevin").then(resolve).then(resolve2).catch((error)=>{
console.log("error", error);});
使用 Promise 的时候,业务逻辑代码都在 newPromise 和 then 的回调方法中;
// 创建 XMLHttpRequest 对象
var xmlhttp = new XMLHttpRequest();
// 和服务器建立连接
xmlhttp.open("method", "url", async);
/*
method: 请求的类型, GET POST
url: url 地址
async: true(异步), false(同步);
*/
// 发送请求
xmlhttp.send(string); // string 仅用于 post 请求
在异步 async 为 true 时,需要设置 XMLHttpRequest 对象的 onreadystatechange 一个回调函数;或者设置 XMLHttpReuqest 对象的 onload 一个回调函数;
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState === 4) && (xmlhttp.status === 200){
console.log(xmlhttp.responseText);
}
}
// or
xmlhttp.onload = function(){
if (xmlhttp.readyState === 4) && (xmlhttp.status === 200){
console.log(xmlhttp.responseText);
}
}
简单示例
python 服务端代码
# -*- coding: UTF-8 -*-
__author__ = "Kevin"
__time__ = "2021/8/26"
__blog__ = "https://kevinspider.github.io/"
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def root():
return {"message": "Hello World"}
html 请求页面
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="../snip/vue.js"></script>
</head>
<body>
<div id="root">
<button id="request" v-on:click="promiseOnloadRequest">发送请求</button>
</div>
</body>
<script>
var vue = new Vue({
el: "#root",
methods: {
request: function () {
// 发送请求
var xmlhttp = new XMLHttpRequest();
// 设置回调函数
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
console.log("success: ", xmlhttp.status);
console.log("xmlhttp.responseText: ", xmlhttp.responseText);
}
}
xmlhttp.open("GET", "http://127.0.0.1:8000", true);
xmlhttp.send()
},
promiseRequest: function () {
var promise = new Promise(function (resolve, reject) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if ((xmlhttp.readyState === 4) && (xmlhttp.status == 200)) {
resolve(xmlhttp.responseText);
}
}
xmlhttp.open("GET", "http://127.0.0.1:8000", true);
xmlhttp.send()
})
function f(res) {
console.log("use promiseRequest");
console.log("res", res);
}
promise.then(f).catch((error) => {
console.log(error);
})
},
promiseOnloadRequest: function () {
var promise = new Promise(function (resolve, reject) {
var xml = new XMLHttpRequest();
xml.open("GET", "http://127.0.0.1:8000", true);
xml.onload = resolve;
xml.send()
});
function parseResponse(res) {
console.log("promiseOnloadRequest", res);
}
function parseError(error) {
console.log("error", error);
}
promise.then(parseResponse).catch(parseError);
}
}
});
</script>
</html>
事件名称参照:https://developer.mozilla.org/zh-CN/docs/Web/Events
事件传播
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>节点</title>
</head>
<body>
<div id="div">
<p id="p">点击触发</p>
</div>
</body>
<script>
var phases = {
"1": "capture", // 捕获阶段
"2": "target", // 目标阶段
"3": "bubble" // 冒泡阶段
}
var div = document.getElementById("div");
var p = document.getElementById("p");
function callback(event) {
let tag = event.currentTarget.tagName;
let phase = phases[event.eventPhase];
console.log("tag", tag);
console.log("phase", phase);
}
// 当 addEventListener 第三个参数设置为 true 时, 会先触发父节点的事件, phase 为 capture; 再触发 target 目标, phase 为 target;
// div.addEventListener("click", callback, true);
// p.addEventListener("click", callback, true);
// 当 addEventListener 第三个参数设置为 false 时, 会先触发子节点的事件, phase 为 target; 再触发冒泡, 回传到父节点, phase 为 bubble;
div.addEventListener("click", callback, false);
p.addEventListener("click", callback, false);
</script>
</html>
Event 对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>节点</title>
</head>
<body>
<div id="div">
<p id="p">点击触发</p>
</div>
</body>
<script>
var phases = {
"1": "capture", // 捕获阶段
"2": "target", // 目标阶段
"3": "bubble" // 冒泡阶段
}
var div = document.getElementById("div");
var p = document.getElementById("p");
function callback(event) {
let tag = event.currentTarget.tagName;
let phase = phases[event.eventPhase];
console.log("tag", tag);
console.log("phase", phase);
}
var event = new Event("look", {
bubbles: true,
cancelable: true
});
p.addEventListener("click", callback, false);
p.dispatchEvent(event);
</script>
</html>
监听函数
HTML 的 on - 属性
<div id="button" onclick="btnClick()">点击触发</div>
<script>
function btnClick(event) {
console.log("event", event);
console.log("button clicked");
}
</script>
元素节点的事件属性
<div id="button">点击触发</div>
<script>
var button = document.getElementById("button");
button.onclick = function (event) {
console.log("event", event);
console.log("按钮点击");
}
</script>
EventTarget.addEventListener()
<div id="button">点击触发</div>
<script>
var button = document.getElementById("button");
function btnClick(event) {
console.log("event", event);
console.log("addEventListener", "clicked");
}
button.addEventListener("click", btnClick);
</script>
浏览器中,window 对象指的是当前的浏览器窗口;它也是当前页面的顶层对象,它也是当前页面的顶层对象,即最高一层的对象,所有其他对象都是它的下属,一个变量如果未声明,那么默认就是顶层对象的一个属性;
Document 常用方法
navigator 对象的属性:
navigator 对象的方法
cookie 的生成
Storage 属性
Storage 方法
location 对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法;通过 window.location 或者 document.location 都可以获得这个对象;
location 属性
location 方法
var url = "https://www.baidu.com?q=你好";
encodeURI(url);
// "https://www.baidu.com?q=%E4%BD%A0%E5%A5%BD"
var url = "https://www.baidu.com?q=你好";
encodeURIComponent(url);
// "https%3A%2F%2Fwww.baidu.com%3Fq%3D%E4%BD%A0%E5%A5%BD"