JavaScript中的30个疑难杂症

目录

  1. 数据类型
  2. 表达式运算符和分支结构
  3. 内置对象
  4. JS DOM
  5. JS BOM
  6. 函数对象
  7. 面向对象

typeof 和 instanceof

JS数据类型:

  • 原始类型(基本类型)Undefined Null Boolean Number String
  • 引用类型(复杂类型)Object

1、typeof检测返回对应数据类型

console.log(typeof 123); // number
console.log(typeof true); // boolean
console.log(typeof "hello"); // string
console.log(typeof undefined); // undefined
console.log(typeof null); // object
// 计算机typeof 按照机器码后3位判断数据类型
// null 00000000 => object后3位为0console.log(typeof []); // object
console.log(typeof {});  // object
console.log(typeof new Date()); // object
console.log(typeof function () {}); // function
console.log(typeof Array); // function
// typeof 引用类型 object function
// object + call方法 => function

字符串示例

var name1 = "Tom";
console.log(name1); // Tom
console.log(typeof name1);  //stringvar name2 = new String("Tom");
console.log(name2);
// String {"Tom"}
// 0: "T"
// 1: "o"
// 2: "m"
// length: 3
console.log(typeof name2); // object

总结:

typeof 少null, 多function

2、instanceof检测返回bool: true/false

console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
function Person() {}
console.log(new Person() instanceof Person); // trueconsole.log([] instanceof Object); // true
console.log(new Date() instanceof Object); // true
console.log(new Person() instanceof Object); // true

instanceof 原型链查找

A instanceof B == True
B instanceof C == True=> A instanceof C == True
console.log(Object.prototype.toString.call("1")); // [object String]
console.log(Object.prototype.toString.call([])); // [object Array]

总结:

  1. typeof 返回值是一个字符串:

    • number string boolean
    • function(函数)
    • object(null,数组,对象)
    • undefined
  2. instanceof 返回布尔值,判断A 是否为B 的实例对象,检测的是原型

数据的存储形式-堆栈

js堆栈

  • 栈:计算机为原始类型开辟的一块内存空间 string number…
  • 堆:计算机为引用类型开辟的一块内存空间 object
// 栈中存放数值
var a = "Tom";
var b = a;
b = "Jack";
console.log(a, b);
// Tom Jack// 栈中存放地址值,堆中存放对象
var c = { key: "value" };
var d = c;
d.key = "newValue";
console.log(c, d);
// {key: "newValue"}  {key: "newValue"}

深拷贝和浅拷贝

  • 深拷贝:修改复制对象,原始对象不会变化
  • 浅拷贝:修改复制对象,原始对象也变化

方式:

  • 遍历赋值
  • Object.create()
  • JSON.parse()和JSON.stringify()

操作的对象

var obj = {a: "Hello",b: {a: "world",b: 111,},c: [11, "Jack", "Tom"],
};

1、浅拷贝

1-1、遍历赋值

// 浅拷贝
function simpleCopy(o) {var o_ = {};for (let i in o) {o_[i] = o[i];}return o_;
}var newObj = simpleCopy(obj);
newObj.b.a = "WORLD";
console.log(obj);console.log(newObj);/**
obj 和 newObj都变了:
b: { "a": "WORLD", "b": 111}}
*/

1-2、Object.create()

var newObj = Object.create(obj);
newObj.b.a = "WORLD";console.log(obj);
// b: {a: "WORLD", b: 111}
console.log(newObj);
// __proto__:
// b: {a: "WORLD", b: 111}

2、深拷贝

2-1、遍历赋值

function deepCopy(object, deepObject=null) {let deepObj = deepObject || {};for (let i in object) {if (typeof object[i] === "object") {// 引用类型 [] {} nullif(object[i] === null){deepObj[i] = object[i];} else{deepObj[i] = object[i].constructor === Array ? []: {}// deepObj[i] = object[i].constructor === Array ? []: Object.create(null);deepCopy(object[i], deepObj[i])}} else{// 简单数据类型deepObj[i] = object[i];}}return deepObj;
}var newObj = deepCopy(obj);
newObj.b.a = "WORLD";console.log(obj);
// b: {a: "world", b: 111}
console.log(newObj);
// b: {a: "WORLD", b: 111}

2-2 JSON

function deepCopy(o) {return JSON.parse(JSON.stringify(o));
}var newObj = deepCopy(obj);
newObj.b.a = "WORLD";console.log(obj);
// b: {a: "world", b: 111}
console.log(newObj);
// b: {a: "WORLD", b: 111}

数据类型转换

1、 特殊类型的隐式转换 NaN, 0, undefined, null, “” => false

Boolean(NaN) // falseBoolean(null) // falseBoolean(undefined) // falseBoolean(0) // falseBoolean("") // falseBoolean([])  // trueBoolean({}) // true

2、 逻辑运算符&&||

console.log(true && true) // trueconsole.log(false || false) // falseconsole.log(5 || 0) // 5console.log(0 || 5) // 5

运用


var a = 0;if(a === 0){console.log(a);
} else{console.log(5);
}// 等价于
console.log(a && 5);

3、 == 和 ===

== 比较值
=== 比较值 和 类型

console.log(undefined == null); // true
console.log(undefined === null); // falseconsole.log(0 == '0'); // true
console.log(0 === '0'); // false

多运算符和分支结构

1、运算符的优先级

+-/* && || () -= -- ++增加括号

2、舍入误差

console.log(1.0 + 2.0)
// 3console.log(0.1 + 0.2)
// 0.30000000000000004// 转二进制
(0.1).toString(2)
"0.0001100110011001100110011001100110011001100110011001101"

舍入误差解决

// 方案一: 舍去后面的位数
console.log(parseFloat((0.1 + 0.2).toFixed(2)));
// 0.3// 方案二
function add(num1 , num2){let n = Math.pow(10, 2); // 增大一定倍数,使得两个数进行整数计算return ((num1 * n) + (num2 * n)) / n
}console.log(add(0.1, 0.2));
// 0.3

优化for循环

性能优化

var array = [];for (let index = 0; index < array.length; index++) {// do something
}// 优化后
for (let index = 0, len = array.length; index < len; index++) {// do something
}

算法优化

// 求和:1 + 2 + 3 + 4 +... + 100
var sum = 0;
for (let i = 1; i <= 100; i++) {sum += i;
}
console.log(sum); // 5050// 等差数列公式求和公式 Sn=n(a1+an)/2
console.log(((1 + 100) * 100) / 2); // 5050

例题:找出两个数,和为11,返回下标

var list = [1, 7, 3, 4, 5, 6];

方式一:

var loop = 0;for (let i = 0; i < list.length; i++) {for (let j = 0; j < list.length; j++) {if (list[i] + list[j] == 11) {console.log(i, j);// 1 3, 3 1, 4 5, 5 4}console.log("loop", ++loop);// loop 36}
}

方式二:

var loop = 0;
for (let i = 0; i < list.length; i++) {let index = list.indexOf(11 - list[i]);if (index > -1) {console.log(i, index);// 1 3, 3 1, 4 5, 5 4}console.log("loop", ++loop);// loop 6
}

js中常见的内置对象

1、三种包装对象:String, Number, Boolean


// 内置对象调用方法
var str = "Hello";var str_ = new String('Hello')
str = str_.toUpperCase()
str_ = nullstr.toUpperCase()

火狐浏览器可以打印出对象的方法


console.log(Number)
console.log(String)
console.log(Boolean)

2、其他标准内置对象:Array, Date, Function, Object…

装箱和拆箱

  • 装箱:基本数据类型 -> 引用数据类型
var num = 123;
var numObj = new Number(123);console.log(typeof num) // number
console.log(typeof numObj) // object
  • 拆箱:引用数据类型 -> 基本数据类型
var numObj = new Number(123);console.log(numObj.valueOf()) // 123console.log(typeof numObj.valueOf()) // number

拆箱操作原理:

内部执行toPrimitive(input, type)
input 传入的值
type 值类型1. 如果是原始类型的值直接返回
2. 如果不是,调用 input.valueOf() 是原始类型就返回
3. 如果不是,继续调用  input.toString() 是原始类型就返回
4. 报错
valueOf()  有原始类型的值返回,没有返回对象本身
toString() 对象[object type] type:对象类型

例题1:

console.log([] + [])
<empty string>// 分析:
console.log([])
Array []console.log([].valueOf())
Array []console.log([].toString())
<empty string>

例题2:

console.log([] + {})
[object Object]// 分析:
console.log({}.valueOf())
{}console.log({}.toString())
[object Object]// 交换位置,{}可能被识别为代码块
console.log({} + [])
[object Object] 或 0console.log(+ [])
0console.log(+ '')
0console.log(+ {})
NaN

深入理解栈和队列

  • 栈: 后进先出 LIFO (last in first out)

  • 队列: 先进先出 FIFO (first in first out)

  • 栈和堆:数据存储

  • 栈和队列:数据访问顺序

js数组 具备了 栈 + 队列

push
pop
unshift
shift

var list = [1, 2, 3];// 队尾入栈
list.push(4);
console.log(list); // [ 1, 2, 3, 4 ]var val1 = list.pop();
console.log(list); // [ 1, 2, 3 ]
console.log(val1);  // 4// 队首入栈
list.unshift(0);
console.log(list);  // [0, 1, 2, 3]var val2 = list.shift();
console.log(list);  // [1, 2, 3]
console.log(val2);  // 0
  • 结尾出入栈,不影响原有数据位置索引,效率高
  • 开头出入栈,会影响原有的数据位置索引,效率低

sort列表排序

var list1 = [1, 3, 2, 5, 8];
console.log(list1.sort()); // [1, 2, 3, 5, 8]// 得到不期望的排序结果
var list2 = [3, 23, 15, 9, 31];
console.log(list2.sort());  // [15, 23, 3, 31, 9]

sort:

  1. 默认升序
  2. 按照字符串Unicode码进行排序

解决

定义一个比较器函数

function compare(x, y) {return x - y;
}var list1 = [1, 3, 2, 5, 8];
console.log(list1.sort(compare)); // [1, 2, 3, 5, 8]// 得到期望的排序结果
var list2 = [3, 23, 15, 9, 31];
console.log(list2.sort(compare)); //  [3, 9, 15, 23, 31]

Date对象中的getMonth()

// 2021-03-15 星期一
var now = new Date();
console.log(now.getTime()); // 13位时间戳,1970年1月1日至今的毫秒数
//   单位是毫秒: 1615820418925console.log(now.getDate()); // 本月第几号:  15
console.log(now.getDay()); //  本周第几天1-7:1
console.log(now.getMonth() + 1); // 月份:3

客户端的时间可以修改
严谨的时间需要后端给

开发中的编码和解码

  • escape/unescape
  • encodeURI/decodeURI
  • encodeURIComponent/decodeURIComponent

1、escape/unescape

处理ASCII码表之外的字符

var url = "http://www.baidu.com?name=张三&age=23";
console.log(escape(url));
//   http%3A//www.baidu.com%3Fname%3D%u5F20%u4E09%26age%3D23var escapeUrl = "http%3A//www.baidu.com%3Fname%3D%u5F20%u4E09%26age%3D23";
console.log(unescape(escapeUrl));
// http://www.baidu.com?name=张三&age=23

2、encodeURI/decodeURI(用的较多)

处理unicode编码

var url = "http://www.baidu.com?name=张三&age=23";
console.log(encodeURI(url));
// http://www.baidu.com?name=%E5%BC%A0%E4%B8%89&age=23var escapeUrl = "http://www.baidu.com?name=%E5%BC%A0%E4%B8%89&age=23";
console.log(decodeURI(escapeUrl));
// http://www.baidu.com?name=张三&age=23

3、encodeURIComponent/decodeURIComponent

var url = "http://www.baidu.com?name=张三&age=23";
console.log(encodeURIComponent(url));
// http%3A%2F%2Fwww.baidu.com%3Fname%3D%E5%BC%A0%E4%B8%89%26age%3D23var escapeUrl = "http%3A%2F%2Fwww.baidu.com%3Fname%3D%E5%BC%A0%E4%B8%89%26age%3D23";
console.log(decodeURIComponent(escapeUrl));
// http://www.baidu.com?name=张三&age=23

理解DOM树的加载过程

浏览器发起请求过程

浏览器URL  -> DNS域名解析 -> IP所在服务器发起请求

浏览器处理响应过程

html:二进制转为html构建DOM树:Html解析: Token -> Node -> DOMToken词法解析: 根是document对象 <div></div>Node:HTML div ElementDOM: DOM和标签是一一对应的关系解析过程中:link css并行下载script 先执行js,完成后继续构建DOM树底部引入js 头部引入js, 加async,deferasync: 异步下载js文件,不影响DOM解析,下载完成后尽快执行jsdefer:文档渲染完后,DOMContentLoaded时间调用之前,按照顺序执行jswindown.onload构建css树:CSS解析器每个css文件解析为CSSStyleSheet样式表对象,每个对象都包含CSSRule, CSSRule包含选择器和声明对象Token解析->Node->CSSOM构建Render树:渲染树 = DOM树 + CSS树布局layout和绘制paint: 计算对象之间的大小,确定每个节点在屏幕上的坐标映射浏览器屏幕绘制,使用UI后端层绘制每个节点reflow 回流:元素属性发生变化且影响布局时(高、宽、内外边距等)相当于刷新页面repaint 重绘:元素属性发生变化且不影响布局时(颜色、透明度、字体样式等)相当于不刷新页面,动态更新内容重绘不一定引起回流,回流必将引起重绘

3种事件绑定的异同

html事件
dom0事件
dom2事件

  • 广义javascript ECMAScript + DOM + BOM DOM0 DOM1 DOM2
  • 狭义javascript ECMAScript ES6 ES5 ES3

事件监听的优点:可以绑定多个事件,常规的事件绑定只执行最后绑定的事件
事件绑定:相当于存储了函数地址,再绑定一个事件,相当于变量指向了另一个函数地址
事件监听:相当于订阅发布者,改变了数据,触发了事件,订阅这个事件的函数被执行

addEventListener函数

element.addEventListener(event, function, useCapture)
removeEventListener()event        (必需)事件名
function     (必需)事件触发函数
useCapture   (可选)指定事件在捕获(tru)或冒泡(false)阶段执行IE8: element.attathEvent(event, function)event    (必需)事件名, 需加'on' eg: onclick
function (必需)事件触发函数
<button onclick="func1()">Html事件</button>
<button id="btn0">事件绑定</button>
<button id="btn2">事件监听</button><script>function func1() {console.log("func1");}function func2() {console.log("func2");}function func3() {console.log("func3");}function func4() {console.log("func4");}function func5() {console.log("func5");}// dom0级事件:事件绑定; 只执行func3document.getElementById("btn0").onclick = func2;document.getElementById("btn0").onclick = func3;// dom2级事件:事件监听; 两个函数都会执行document.getElementById("btn2").addEventListener("click", func4);document.getElementById("btn2").addEventListener("click", func5);
</script>

事件触发、事件捕获与事件冒泡

事件捕获与事件冒泡

向下是捕获阶段
---------------|    ^
---------------V    ^
---------------V    |
---------------
向上是冒泡阶段

事件对象:

事件触发时包含了事件发生的元素和属性信息

var div3 = document.getElementById("div3");
div3.addEventListener("click", function (e) {var e = e || window.event; // IE 8  window.event arguments[0]console.log(e);
}, false); // true: 捕获, false: 冒泡(默认)

事件的周期

--------------------
div1                |
---------------     |
div2           |    |
--------       |    |
div3   |       |    |
--------       |    |
---------------     |
--------------------
<style>
#div1 {width: 300px;height: 300px;background-color: green;
}#div2 {width: 200px;height: 200px;background-color: blue;
}#div3 {width: 100px;height: 100px;background-color: grey;
}
</style><div id="div1">div1<div id="div2">div2<div id="div3">div3</div></div>
</div><script>
// 事件对象:时间触发时包含了事件发生的元素和属性信息
var div3 = document.getElementById("div3");
div3.addEventListener("click",function (e) {console.log("div3");},false
);var div2 = document.getElementById("div2");
div2.addEventListener("click",function (e) {console.log("div2");},false
);var div1 = document.getElementById("div1");
div1.addEventListener("click",function (e) {console.log("div1");},false
);/**
* 点击div 3
*
* div3 -> div2 -> div1
*/
</script>

阻止冒泡:

e.stopPropagation()e.cancelBubble = true // IE8

事件冒泡的应用:事件委托

<div id="demo"><li>aaaaaa</li><li>bbbbbb</li><li>cccccc</li>
</div><script>// 事件委托var demo = document.getElementById("demo");demo.addEventListener("click", function (e) {if (e.target.nodeName.toLowerCase() == "li") {console.log(e.target.innerHTML);}}, false );</script>

阻止默认行为的两种方式

  • e.preventDefault()
  • return false

让a标签链接不跳转

<a href="https://www.baidu.com/">百度</a><script>var a = document.querySelector('a')a.onclick = function(e){// 方式一// e.preventDefault()// 方式二return false;}
</script>

让form表单不提交

<form action="/post"><input type="submit" value="提交" id="submit"/>
</form><script>var submit = document.getElementById("submit");submit.onclick = function (e) {// 方式一// e.preventDefault()// 方式二return false;};
</script>

使用History和location

1、History

window.hostory 属性指向History对象

表示当前窗口的浏览历史

类似栈的数据结构

History:back()forward()go() 0 -1 -2pushState()replaceState()

2、Location

window.location和document.location

Location:href:      整个URLprotocal   URL协议,包括冒号host       主机 包括主机名,冒号:,端口hostname   主机名,不包括端口port       端口号pathname   URL的路径部分,从根路径/ 开始search     查询字符串,问号?开始hash       片段字符串 从#开始username   用户名password   密码origin     URL协议,主机名,端口号

常见函数的4种类型

  • 匿名函数
  • 回调函数
  • 递归函数
  • 构造函数

1、匿名函数

定义时候没有任何变量引用的函数

匿名函数自调:函数只执行一次

(function(a, b){console.log(a + b);}
)(1, 2);// 等价于
function foo (a, b){console.log(a + b);
}foo(1, 2);

jQuery:

(function(window, undefined){var jQuery;...window.jQuery = window.$ = jQuery;
})(window);

优点:节约内存空间,掉用前和调用后内存中不创建任何函数对象

2、回调函数callback

如果一个函数作为对象交给其他函数使用

var arr = [33, 9, 11, 6];arr.sort(function (a, b) {return a - b;
});console.log(arr);
// [6, 9, 11, 33]

异步回调

function getPrice(params, callback){$.ajax({url: '/getPrice',type: 'POST',data: params,success: function(data){callback(data);}})
}

3、递归函数

循环调用函数本身

var func = function(x) {if(x === 2){return x} else{return x * f(x - 1)}
}

arguments.callee 严格模式下不支持使用 use strict

function func(x){if(x === 1){return 1} else{return x * arguments.callee(x -1)}
}

4、构造函数

构造函数习惯上首字母大写

调用方式不一样,作用也不一样

构造函数用来新建实例对象

Person 既是函数名,也是这个对象的类名

function Person(){} // 构造函数new Person()function person(){} // 方法

变量和函数提升

  • js解释执行
  • 变量和函数提升

变量声明提前,函数声明提前

  • 变量声明提前:值停留在本地
  • 函数声明提前:整个函数体提前

如果是var赋值声明的函数,变量提前,函数体停留在本地

1、变量提升

未声明使用会报错

console.log(a); // Error: a is not defined

var会变量提升

console.log(a);  // undefined
var a = 10;

let定义不会提升

console.log(a);  // Error: Cannot access 'a' before initialization
let a = 10;

2、函数提升

console.log(func);  // func(){}
function func(){}
console.log(foo);  // undefined
var foo = function func(){}
console.log(func);  // Error: func is not defined
var foo = function func(){}

作用域和作用域链

  • 全局作用域,函数作用域

  • 作用域链

  • 作用域scope: 一个变量的可用范围

  • 作用域链scope chain:以当前作用域的scope属性为起点,依次引用每个AO,直到window结束,行成多级引用关系

js作用域ES5

  • 全局作用域 window
  • 函数作用域 function(){}

js的变量和函数作用域是在定义是决定的,而不是运行时决定
js的变量作用域在函数体内有效,无块作用域

作用域示例

function a() {function b() {var bb = "bb";}b()var aa = "aa";
}a()console.log(bb);  // Error: bb is not defined
console.log(aa);  // Error: aa is not defined
[[scope]]

作用域链

var cc = 'cc'function a() {function b() {var bb = "bb";console.log(aa); // undefinedconsole.log(cc); // cc}b()var aa = "aa";console.log(cc); // cc
}a()

面试题

var buttons = [{name: 'n1'}, {name: 'n2'}, {name: 'n3'}]function bind(){for(var i = 0; i < buttons.length; i++){buttons[i].func = function(){console.log(i);}}
}bind()buttons[0].func() // 3
buttons[1].func() // 3
buttons[2].func() // 3
function bind(){for(var i =0; i < buttons.length; i++){let num = i;buttons[i].func = function(){console.log(num);}}
}
// 输出 0 1 2

执行环境

浏览器环境栈:js是一个单线程程序
执行环境(执行上下文):EC execution context

  • 全局执行环境

  • 局部执行环境

  • 变量对象:VO variable object 保存全局环境下变量的对象

  • 活动对象:AO activation object 保存函数环境中的变量

重载和多态的使用场景

重载:定义相同名称,不同参数的函数,程序调用时自动识别不同参数的函数
实现了相同函数名不同的函数调用

js中没有重载,可以通过arguments实现函数重载

/*** 计算正方形或长方形面积*/
function React() {if (arguments.length == 1) {// 如果是1个参数,返回正方形this.width = arguments[0];this.height = arguments[0];} else {// 如果是2个参数,返回长方形this.width = arguments[0];this.height = arguments[1];}this.toString = function () {return `${this.width} * ${this.height} = ${this.width * this.height}`;};
}var react1 = new React(5);
console.log(react1.toString());
// 5 * 5 = 25var react2 = new React(3, 4);
console.log(react2.toString());
// 3 * 4 = 12

多态:同一个东西在不同情况下表现不同状态,重写和重载

闭包

闭包的概念Closure:作用域

引用了自由变量的函数,这个被引用的自由变量将和这个函数一同存在;
即使已经离开了创造它的环境也不例外。
所以,闭包是由函数和其他相关的引用环境组合而成,实现信息驻留;
信息的保存,引用在,空间不销毁

简单的使用

var Person = function () {var count = 0;return function () {return count++;};
};var p = Person()
console.log(p()); // 0
console.log(p()); // 1
console.log(p()); // 2

闭包的应用

var buttons = [{name: 'n1'}, {name: 'n2'}, {name: 'n3'}]function bind() {for (var i = 0; i < buttons.length; i++) {// 定义一个立即执行函数,行成闭包(function (num) {buttons[i].func = function () {console.log(num);};})(i);}
}bind();buttons[0].func(); // 0
buttons[1].func(); // 1
buttons[2].func(); // 2

闭包缺点:

闭包导致内存驻留,如果是大量对象的闭包环境需要注意内存消耗

ES6中使用let定义局部变量也可以实现输出0 1 2

function bind() {for (let i = 0; i < buttons.length; i++) {buttons[i].func = function () {console.log(i);};}
}

call、apply、bind的使用场景区分

call、apply、bind都是Function对象的方法

1、apply调用一个函数,可以指定this值

Function.apply(obj, args)obj: 这个对象将替代Function类里的this对象
args: 是一个数组

2、call

Function.call(obj, ...args)
args: 单个参数
var stu1 = {name: "Tom",say: function (age, school) {console.log(this.name, age, school);},
};var stu2 = {name: "Jack",
}stu1.say(18, '清华'); // Tom 18 清华
stu1.say.call(stu2, 28, '北大'); // Jack 28 北大
stu1.say.apply(stu2, [28, '北大']); // Jack 28 北大

类数组转数组

var arr = Array.prototype.slice.apply(arguments)

3、bind:

类似call, 不同之处在于call调用之后立即执行,bind需要一个变量进行接收之后再执行

new的执行过程

var Person = function(name, age){this.name = name;this.age = age;
}var person =new Person('Tom', 23);
console.log(person.name); // Tom// 分4步
// 1、创建一个新对象obj
var obj = new Object();// 2、把obj的proto指向构造函数的prototype对象,实现继承
obj.__proto__ = Fn.prototype;// 3、将this指向obj
var result = Fn.call(obj)// 4、返回创建的obj,如果该函数没有返回对象,则返回this
if(typeof result === 'object'){return result; // func = result
} else{return obj;  // func = obj
}

this的使用

this指向

指代当前调用的这个对象

4中绑定(优先级从低到高):

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new绑定
var person = {name: 'Tom',age: 23,showName: function () {// this->personconsole.log(this.name); // Tom},showAge: function () {// 局部函数function _age(){// this->windowconsole.log(this.age); // undefined}_age();// this->personconsole.log(this.age); // 23},
};person.showName()
person.showAge()

可以先保存this

let that = this;

改变this指向

call/apply/bind

var name = 'Tom'var person = {name: 'Jack',showName: function(){console.log(this.name);}
}person.showName(); // Jack// this->window
var show = person.showName;
show(); // Tomvar fn = person.showName.bind(person);
fn(); // Jack

实现一个bind方法

Function.prototype.bind = function(obj){var that = this;return function(){that.apply(obj)}
}// 验证
var name = 'Tom'var person = {name: 'Jack',showName: function(){console.log(this.name);}
}var fn = person.showName.bind(person);
fn(); // Jack

理解面向对象

对象:

  • 具备私有属性 {name: ‘Tom’}
  • 只要是new出来的都是对象 new Func() => 实例化
  • 不同对象可定不相等
  • 对象都会有引用机制

js万物皆对象

  1. 字面量 - 字面显示的内容 Array Date Object
  2. 包装类 - 没有new的函数声明,可以理解为不是对象 String, NUmber

面向对象:

把任何的数据和行为抽象成一个形象的对象

面向对象OOP:继承 封装 多态

java: classjs: function Person(){}; new Person()
  • 继承:子继承父
  • 封装:方法function()
  • 多态: 重载、重写(继承)

原型和原型链

创建对象

// 1、函数对象
var func = new Function("str", "console.log(str)");
func("hi"); // hi// 2、普通对象
var obj1 = {name: "Tom",getName: function () {return this.name;},
};
console.log(obj1.getName()); // Tomvar obj2 = new Object();// 3、构造函数创建对象
function Person(name) {this.name = name;this.getName = function () {return this.name;};
}var p1 = new Person('Tom');
var p2 = new Person('Jack');console.log(p1.getName()); // Tom
console.log(p2.getName()); // Jack

原型和原型链

  1. 一句话:万物皆对象,万物皆空null
  2. 两个定义:
  • 原型:保存所有子对象的公有属性值和方法的父对象
  • 原型链:由各级子对象的__proto__ 属性连续引用行成的结构
  1. 三个属性:__proto__、constructor、prototype
// 构造函数实现类
function Person(name, age) {this.name = name;this.age = age;this.say = function () {console.log(this.name + this.age);};
}// 1、当函数创建的时候就会携带prototype属性,指向原型对象
Person.prototype.money = 20;Person.prototype.run = function(){console.log('run...');
}// constructor
console.log(Person.prototype.constructor === Person); // truevar p1 = new Person("Tom", 18);// 所有对象都会携带 __proto__
console.log(p1.__proto__ === Person.prototype); // true
  1. 挂载在函数内部的方法上,实例化对象内部会复制构造函数的方法
  2. 挂载在原型上的方法,不会复制
  3. 挂载在内部和原型上的方法都是可以通过实例去调用的
  4. 一般来说,如果需要访问构造函数内部的私有变量,我们可以定义再函数内部;
    其他情况可以定义再函数的原型上

总结

  1. 所有对象都携带obj.__proto__
  2. obj.__proto__ === Person.prototype
  3. Person.prototype.constructor === Person

原型相关API

Function和Object关系

function Person(){}
var person = new Person()=>
person.__proto__ -> Person.prototype
Person.prototype.constructor -> PersonFunction.__proto__ -> Function.prototypePerson.__proto__ -> Function.prototype
Object.__proto__ -> Function.prototype
Function.prototype.__proto__ -> Object.prototype
Object.prototype.__proto__ -> null翻一下:
令:
__class__ = __proto__
Person[class] = Person.prototypeperson.__class__ -> Person[class]
Person[class].constructor ->  PersonFunction.__class__ -> Function[class] // 特殊Person.__class__ -> Function[class]
Object.__class__ -> Function[class]  // 特殊
Function[class].__class__ -> Object[class]
Object[class].__class__ -> null就是一个实例找类的过程,有特殊

Function对象和Object对象之间的关系

  • Function是顶层的构造器,Object是顶层的对象
  • 顶层有null, Object.prototype, Function.prototype Function
  • 原型上说:Function继承了Object
  • 构造器上说:Function构造了Object

原型相关API判断对象的属性是自有还是私有

  • hasOwnProperty
  • isPropertyOf 判断对象是否在原型链中
  • getPropertyOf 获取原型对象的标准方法

继承

继承的方式

继承的6种方式

  1. 简单原型链:类式继承
  2. 借用构造函数:缺点=> 父类的原型方法自然不会被子类继承
  3. 组合继承(最常用):类式继承+构造函数式继承
  4. 寄生组合继承(最佳方式):寄生式继承+构造函数式继承
  5. 原型式:跟类式继承一样,父对象Book中的值类型的属性被复制,引用类型的属性被共有
  6. 寄生式:通过在一个函数内的过渡对象实现继承并返回新对象的方式

继承的应用

Object.defineProperty

定义

Object.defineProperty(obj, prop, descriptor)/*
obj:需要定义属性的对象
prop:需要定义的属性
descriptor:属性的描述描述符
返回值:返回此对象
*/
var obj = {}// 数据描述符
var descriptor = {// 能否delete删除,configurable: false,// 是否可写,默认false, 不能被赋值,只读writable: false,// 是否可枚举,即是否可以for...in访问属性,默认falseenumerable: false,// 属性值,默认undefinedvalue: 'hello',// 访问器描述符,不能与数据描述符同时使用// get: 读取,默认undefined// set: 设置,默认undefined
}Object.defineProperty(obj, 'name', descriptor)
console.log(obj.name)

示例:数据响应式 vue

function defineReactive(obj, key, val) {// val,由于闭包的存在,不会被销毁Object.defineProperty(obj, key, {get() {console.log('get');return val;},set(newVal) {if (newVal != val) {console.log('set');val = newVal;}},});
}var obj = {};
defineReactive(obj, 'foo', '123')
console.log(obj.foo); // get  123obj.foo = '223' // set
console.log(obj.foo);  // get 223

笔记:JavaScript中的30个疑难杂症相关推荐

  1. js学习笔记----JavaScript中DOM扩展的那些事

    什么都不说,先上总结的图~ Selectors API(选择符API) querySelector()方法 接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null. ...

  2. 小白编程笔记——JavaScript中两种把表单内容传递给Controller的方法

    工作的时候看到有两种把页面上内容传递给Controller的方法,其中一种是传递对数据库的搜索条件,并且会根据搜索条件访问数据库,并将更新后的数据写在JqGrid表格里.另一种则是用于为数据库新增数据 ...

  3. PHP笔记-JavaScript中使用Smarty变量

    如果Smarty传来的变量是字符串时: $strData = "abc"; $this->assign("strData", strData); Java ...

  4. JavaScript学习笔记06【高级——JavaScript中的事件】

    w3school 在线教程:https://www.w3school.com.cn JavaScript学习笔记01[基础--简介.基础语法.运算符.特殊语法.流程控制语句][day01] JavaS ...

  5. JavaScript学习笔记——JS中的变量复制、参数传递和作用域链

    今天在看书的过程中,又发现了自己目前对Javascript存在的一个知识模糊点:JS的作用域链,所以就通过查资料看书对作用域链相关的内容进行了学习.今天学习笔记主要有这样几个关键字:变量.参数传递.执 ...

  6. html5学习笔记---05.JavaScript 中的面向对象,继承和封装

    05.JavaScript 中的面向对象 a.创梦技术qq交流群:CreDream:251572072 a.JavaScript 是一种基于对象的语言   类:JavaScript 对象很抽象,所以下 ...

  7. 【javascript笔记】关于javascript中的闭包

    最开始看<javascript高级程序设计>的时候就看到了javascript中的闭包,在第七章第二节....好大概知道了,过了段时间,好了又忘了... 我们来看这本书里面关于闭包是怎么描 ...

  8. JavaScript 中的内存和性能、模拟事件(读书笔记思维导图)

    由于事件处理程序可以为现代 Web 应用程序提供交互能力,因此许多开发人员会不分青红皂白地向页面中添加大量的处理程序.在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体 ...

  9. JavaScript 中的继承(读书笔记思维导图)

    继承是 OO 语言中的一个最为人津津乐道的概念.许多 OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.由于函数没有签名,在 ECMAScript ...

最新文章

  1. android studio 开发环境搭建
  2. Bitcoin.com推出BCH新图表,加大对BCH的支持
  3. 使用注解实现ssh整合
  4. 格式化 SQL 来提高效率
  5. 图像超分辨率进ASC19超算大赛,PyTorch+GAN受关注
  6. Python Imaging Library: ImageFilter Module(图像滤波模块)
  7. 《量化交易核心策略开发:从建模到实战》读书笔记
  8. springmvc web.xml和application.xml配置详情(附:完整版pom.xml)
  9. windows下游戏服务器端框架Firefly安装说明及demo运行
  10. tomcat乱码的几种解决
  11. 远程桌面计算机名如何删除,如何删除远程连接记录?如何用电脑识别码实现远程控制?...
  12. java汉字转拼音maven_java汉字转拼音pinyin4j功能实现示例
  13. 汉语数字转换成阿拉伯数字
  14. 公司办公提高无线网络质量解决方案
  15. C语言:L1-009 N个数求和 (20 分)
  16. 2012年腾讯实习生笔试附加题
  17. uniapp editor富文本编辑器,h5富文本编辑器封装成插件
  18. java 新浪短链接_java高仿新浪微博短链接地址生成工具ShortUrlGenerator.java | 学步园...
  19. Unity中国象棋(二)——走棋
  20. 基于python下django框架 实现外卖点餐系统详细设计

热门文章

  1. 从有线通信到无线通信
  2. VMM安装freenas
  3. ai皮肤检测分数_上线俩月,完成2300多万次皮肤测试;找出皮肤问题?AI测肤技术准确率达95%...
  4. Windows 2008 R2上MySQL5.7异常关闭,报错:mysqld got exception 0xc000001d问题解决
  5. C++ mutable关键字
  6. ps cc2018启动界面无响应解决方案
  7. java 实现在线播放_java文档在线播放实现
  8. password authentication failed for user “##““
  9. 一起来创建一个无向图吧!
  10. 虚幻4渲染编程(灯光篇)【第一卷:各种ShadowMap】