- // 基本数据类型
- 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"
-