基本类型:string 、number、boolean、undefined、null、symbol
引用类型:object
数据类型详细内容:https://blog.csdn.net/Zhang_wang_yun/article/details/129959316?spm=1001.2014.3001.5502
考题:
<script>
alert(true + 1) // 2
alert('name' + true) // nametrue
alert(undefined + 1) //NaN
alert(typeof null) // object
alert(typeof NaN); //number
alert(typeof undefined); //undefined
alert(typeof null); //object
</script>
注:
NaN是一个数值类型,但是不是一个具体的数字。
延迟加载:async、defer、setTimeout…
1、默认加载:
让我们从定义没有任何属性的作用开始。HTML 文件将被解析,直到脚本文件命中,此时解析将停止,并且将请求获取文件(如果它是外部文件)。然后,将在恢复解析之前执行脚本。
我们来看这一段代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
<script src="./index.js">script>
head>
<body>
<div class="box">div>
body>
html>
html>
const box = document.querySelector('.box');
console.log(box);
按照上面的解析方式,我们的box是获取不到的
如果我们改变一下顺序,此时就能获取到box
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<div class="box">div>
<script src="./index.js">script>
body>
html>
async在 HTML 解析期间下载文件,并在完成下载后暂停 HTML 解析器以执行该文件。
简单来说就是:async是和html解析同步的(一起的),如果有多个文件,它不是顺次执行js脚本(谁先加载完谁先执行)。
我们可以使用async进行延迟加载解决刚才获取不到dom的问题:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
<script async src="./index.js">script>
head>
<body>
<div class="box">div>
body>
html>
defer在 HTML 解析期间下载文件,并且仅在解析器完成后执行它。 脚本还保证按它们在文档中出现的顺序执行。
简单来说就是:等html全部解析完成,才会执行js代码,顺次执行js脚本。
同样它也能解决刚才的问题:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
<script defer src="./index.js">script>
head>
<body>
<div class="box">div>
body>
html>
async vs defer 官网链接:https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
定时器就不说了,大家应该都很熟悉。
1、==
比较的是值
string == number || boolean || number …都会隐式转换
<script>
console.log(1 == "1"); // true
console.log(true == 1); // true
console.log(null == undefined); // true
console.log([1, 2] == "1,2"); // true
script>
既然它会进行隐式转换,那么我们再来看这一段代码:
<script>
let s = "2";
if (s == 2) {
console.log(typeof s);
}
script>
字符串s和数组2进行比较,通过隐式转换,s它会变成number类型的2,然后进行打印,那么打印的结果应该是我们想的number,但其实并不是。
这是为什么呢?
原因是因为隐式转换它是通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。) 所以最后打印的还是string类型。
我们再来看这一段代码,证明是不是这么一回事:
Object.prototype.valueOf = function () {
alert("111");
};
let arr = [1, 2];
let str = '1,2'
console.log(arr == str);
我们对valueOf进行重写,此时我们再看结果,它变成了false
注:只有当其中一个值为引用类型时,重写才会生效。
<script>
Object.prototype.valueOf = function () {
alert("111");
};
let arr = 1;
let str = "1";
console.log(arr == str);
script>
<script>
console.log(1 === "1"); // false
console.log(false === 1); // false
console.log(null === undefined); // false
console.log([1, 2] === "1,2"); // false
script>
在起初1995年javascript诞生时,最初像java一样,只设置了null作为表示“无”的值,根据C语言的传统,null会被隐式转换成0,这样很不容易发现错误。
并且null像在java里一样被当作一个对象,但是,javascript的数据类型分为基本数据类型和引用数据类型两大类,作者觉得表示“无”的值最好不是对象。
所以作者又设计了一个基本数据类型undefined。
它们的具体区别:JavaScript的最初版本是这样区分的:null是一个表示"无"的对象(空对象指针),转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。
<script>
console.log(Number(null));
console.log(Number(undefined));
script>
在谈微任务和宏任务之前我们先来了解一下javascript。
1、为什么JavaScript是单线程语言?
javascript语言的一大特点就是单线程语言,也就是说,同一个时间只能做一件事情,为什么JavaScript不能有多线程呢?这样能提高效率。
这主要与它的用途有关,作为浏览器脚本语言,javascript的主要用途时与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
比如,假定javascript同时有两个线程,一个线程在某个DOM节点上添加内容,另外一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,javascript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
2、javascript是同步代码,如何执行异步代码?
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前个任务耗时很长,后一个任务就不得不一直等着。
消息队列: 消息队列是一个先进先出的队列,它里面存放着各种消息。
事件循环: 事件循环是指主线程重复从消息队列中取消息、执行的过程
实际上,主线程只会做一件事情,就是从消息队列里面取消息、执行消息,再取消息再执行,当消息队列为空时,就会等待直到消息队列变成非空。
而且主线程只有在将当前的消息执行完成后,才会去取下一个消息。这种机制就叫做事件循环机制,取一个消息并执行。
3、js的执行流程:
同步执行完==》事件循环
同步的任务都执行完了,才会执行事件循环的内容
进入事件循环:请求(ajax)、定时器、事件…
在事件循环中包含:【微任务、宏任务】
比如:
微任务:promise.then
宏任务:setTimeout
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】== 》微任务==》宏任务=》微任务…
我们来看一些代码进行实战演练:
示例1:
<script>
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000 * i);
}
script>
第一次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i);
}, 1000 * 0);
第二次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i);
}, 1000 * 1);
第三次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i);
}, 1000 * 2);
第四次循环,i=3,跳出循环
按照流程:同步==》事件循环【微任务和宏任务】== 》微任务==》宏任务=》微任务…
所有的同步任务执行完,i变成了3,然后依次执行宏任务,所以结果是:
0秒输出3,
1秒后输出3
2秒后输出3
如果将var改为let,那么结果又不一样了。
<script>
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000 * i);
}
script>
第一次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i); i = 0
}, 1000 * 0);
第二次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i); i = 1
}, 1000 * 1);
第三次循环
同步任务: for (var i = 0; i < 3; i++) {
异步任务:
setTimeout(() => {
console.log(i); i = 2
}, 1000 * 2);
第四次循环,i=3,跳出循环
因为 let
关键字拥有块级作用域,i并不会被后面的i重新赋值,因为它们已经不在一个作用域里了,所以结果是:
0秒输出0,
1秒后输出1
2秒后输出2
示例二:
<script>
setTimeout(() => {
console.log("我是定时器");
}, 0);
new Promise((resolve, reject) => {
console.log("我是promise");
resolve();
}).then((res)=>{
console.log('我是then1')
}).then(()=>{
console.log('我是then2');
});
console.log("1");
script>
同步任务:
1、 new Promise((resolve, reject) => {
console.log("我是promise");
resolve();
})
2、 console.log("1");
同步任务执行完进入事件循环
微任务:
then((res)=>{
console.log('我是then1')
}).then(()=>{
console.log('我是then2');
});
清空所有微任务进入宏任务
setTimeout(() => {
console.log("我是定时器");
}, 0);
- 除了函数外,js是没有块级作用域。
- 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。 注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
- 注意声明变量是用var还是没有写(window.)
- 注意:js有变量提升的机制【变量悬挂声明】
- 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
考题一:
<script>
function c() {
var b = 1;
function a() {
console.log(b);
var b = 2;
console.log(b);
}
a();
console.log(b);
}
c();
// 结果:
// undefined
// 2
// 1
script>
考题二:
<script>
var name = "a";
(function () {
if (typeof name == "undefined") {
var name = "b";
console.log("111" + name);
} else {
console.log("222" + name);
}
})();
script>
考题三:
<script>
function fun(a) {
var a = 10;
function a() {}
console.log(a);
}
fun(100);
script>
考题一:
<script>
console.log([1, 2, 3] === [1, 2, 3]); //false
script>
两个数组都是new创建出来的,虽然内容相同,但是地址是不同的,所以为false
考题二:
<script>
var obj1 = {
a: "hellow",
};
var obj2 = obj1; //将obj1直接赋值给obj2,它们指向同一个地址
obj2.a = "world"; //将obj2里面的a值改变,obj1也会跟着变化
console.log(obj1); //{a:world}
(function () {
console.log(a); //undefined
var a = 1; //var定义的变量,会进行变量提升
})();
script>
考题三、
<script>
var a = {};
var b = {
key: "a",
};
var c = {
key: "c",
};
a[b] = "123";
a[c] = "456";
console.log(a[b]); // 456
for(let k in a) {
console.log(k) //[object Object]
}
script>
对象的key值都是字符串类型,在a[b]的时候,它会将b转换为字符串,b作为对象,转换为字符串就变成了【object object】
<script>
function Fn() {
this.a = 'zFn里添加的'
}
Fn.prototype.a = '函数原型添加到'
let obj = new Fn();
obj.a = '对象添加的'
obj.__proto__.a = '对象原型添加的'
console.log(obj.a);
script>
总结:
- 对象是通过new操作符构建出来的,所以对象之间不想等(除了引用外);
- 对象注意:引用类型(共同一个地址);
- 对象的key都是字符串类型;
- 对象如何找属性|方法; 查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
考题一:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
function Foo() {
getName = function () {
console.log(1);
}; //注意是全局的window.
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); //2
getName(); //4
Foo().getName(); // 1
getName(); //1
new Foo().getName(); //3
script>
body>
html>
注:函数加()和不加会造成完全不同的结果。
Foo.getName();
Foo().getName();
我们再来看刚才那道题的答案(加())
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
function Foo() {
getName = function () {
console.log(1);
}; //注意是全局的window.
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo().getName(); //1
getName(); //1
Foo().getName(); // 1
getName(); //1
new Foo().getName(); //3
script>
body>
html>
考题二:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
var o = {
a: 10,
b: {
a: 2,
fn: function () {
console.log(this.a); // 2
console.log(this); //代表b对象
},
},
};
o.b.fn();
script>
body>
html>
考题三:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
window.name = "ByteDance";
function A() {
this.name = 123;
}
A.prototype.getA = function () {
console.log(this);
return this.name + 1;
};
let a = new A();
let funcA = a.getA;
// funcA(); //this代表window
console.log(funcA())
script>
body>
html>
我们将代码改造一下:
让let funcA = a.getA;变为
let funcA = a.getA();
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
window.name = "ByteDance";
function A() {
this.name = 123;
}
A.prototype.getA = function () {
console.log(this);
return this.name + 1;
};
let a = new A();
let funcA = a.getA();
// this指向a
console.log(funcA)
script>
body>
html>
考题四:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<script>
var length = 10;
function fn() {
return this.length + 1;
}
var obj = {
length: 5,
test1: function () {
return fn();
},
};
obj.test2 = fn;
console.log(obj.test1()); //11
console.log(fn() === obj.test2()); //false
console.log(obj.test1() == obj.test2()); //false
script>
body>
html>
方式一:isArray
<script>
let arr = [1, 2, 3];
let str = "123";
let isArray = Array.isArray(arr);
let isArray1 = Array.isArray(str);
console.log(isArray); //true
console.log(isArray1); //false
script>
方式二:instanceof
<script>
let arr = [1, 2, 3];
let str = "123";
let isArray = arr instanceof Array;
let isArray1 = str instanceof Array;
console.log(isArray); //true
console.log(isArray1); //false
script>
方式三:原型prototype
<script>
let arr = [1, 2, 3];
let str = "123";
let isArray = Object.prototype.toString.call(arr).indexOf('Array') > -1;
let isArray1 = Object.prototype.toString.call(str).indexOf('Array') > -1;
console.log(isArray); //true
console.log(isArray1); //false
script>
方式四:isPrototypeOf()
<script>
let arr = [1, 2, 3];
let str = "123";
let isArray = Array.prototype.isPrototypeOf(arr);
let isArray1 = Array.prototype.isPrototypeOf(str);
console.log(isArray); //true
console.log(isArray1); //false
script>
方式五:constructor
<script>
let arr = [1, 2, 3];
let str = "123";
let isArray = arr.constructor.toString().indexOf("Array") > -1;
let isArray1 = str.constructor.toString().indexOf("Array1") > -1;
console.log(isArray); //true
console.log(isArray1); //false
script>
1. slice是来截取的(可截取字符串,也可以截取数组)
参数可以写slice(3)、slice(1,3)、slice(-3)
返回的是一个新的数组
2. splice 功能有:插入、删除、替换
返回:删除的元素
该方法会改变原数组
方式一:new set
<script>
// Array.from转换为真数组
function unique(arr) {
let newArr = new Set(arr);
return Array.from(newArr);
}
console.log(unique([1, 2, 3, 1, 2, 4, 4, 5]));
script>
<script>
// 扩展运算符转化为真数组
function unique(arr) {
let newArr = new Set(arr);
return [...newArr];
}
console.log(unique([1, 2, 3, 1, 2, 4, 4, 5]));
script>
方式二:indexOf
<script>
//创建一个新数组,通过indexOf判断新数组中是否有传入数组中对应的值,
//如果== -1表示没有,则追加进新数组中。
function unique(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
console.log(unique([1, 2, 3, 1, 2, 4, 4, 5]));
script>
方式三:sort
<script>
// 先将传入的数据进行排序,如果当前值不等于前一个值,则追加进新数组
function unique(arr) {
arr = arr.sort();
let newArr = [];
console.log(arr);
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
newArr.push(arr[i]);
}
}
return newArr;
}
console.log(unique([1, 2, 3, 1, 2, 4, 4, 5]));
script>
<script>
// 先创建一个新数组,对传入进来的数据进行遍历循环,
//使用数学Math.max方法查询该数组中的最大值,然后追加进新创建的数组
function fnArr(array) {
let newArr = [];
array.forEach((item, index) => {
newArr.push(Math.max(...item));
});
return newArr;
}
console.log(
fnArr([
[4, 5, 1, 3],
[13, 27, 18, 26],
[32, 35, 37, 39],
[1000, 1001, 857, 1],
])
);
script>
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld
<script>
// 在字符串原型上添加一个addPrefix方法,因为函数里面的this是word,
// 所以只需要将传入的值和this进行拼接即可(谁调用此函数,则this指向谁)
String.prototype.addPrefix = function (prefix) {
return prefix + this;
};
console.log("word".addPrefix("hello"));
script>
- 创建了一个空的对象
我们来验证一下:
<script>
function fn() {
}
console.log(new fn());
script>
- 将空对象的原型,指向于构造函数的原型
<script>
function fn() {}
console.log(fn.prototype == new fn().__proto__);
script>
- 将空对象作为构造函数的上下文(改变this指向)
<script>
function fn() {
console.log(this);
this.name = "东方青云";
}
console.log(fn());
script>
<script>
function fn() {
console.log(this);
this.name = "东方青云";
}
console.log(new fn());
script>
<script>
function fn() {
this.name = "占山";
return 111;
}
console.log(new fn());
script>
<script>
function fn() {
this.name = "占山";
return true;
}
console.log(new fn());
script>
如果renturn 返回的是基本数据类型,则对new操作符没有影响。
<script>
function fn() {
this.name = "占山";
return {name:'哈哈哈'};
}
console.log(new fn());
script>
<script>
function fn() {
this.name = "占山";
return ['哈哈哈哈哈'];
}
console.log(new fn());
script>
如果return 返回的是引用数据类型,则new操作符失效。
我们自己来写一个:
<script>
function fn(name, age) {
this.name = name;
this.age = age;
}
function create(fn, ...args) {
//1. 创建了一个空的对象
let obj = {}; //Object.create({})
//2. 将空对象的原型,指向于构造函数的原型
Object.setPrototypeOf(obj, fn.prototype);
//3. 将空对象作为构造函数的上下文(改变this指向)
let result = fn.apply(obj, args);
//4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log(create(fn, "东方青云", 18));
script>
let str = "aaabbbbbccddddddddddx";
let obj = {}; //先创建一个对象,将字符串转成对象的形式:如{a:3,b:5}........
for (let i = 0; i < str.length; i++) {
let char = str.charAt(i);
// 如果该对象有此字符,则++
if (obj[char]) {
obj[char]++;
} else {
// 没有,则为1次
obj[char] = 1;
}
}
console.log(obj);
// {a: 3, b: 5, c: 2, d: 10, x: 1}
//统计出对象中的最大值
let max = 0;
for (let key in obj) {
if (max < obj[key]) {
max = obj[key];
}
}
console.log(max);
//拿最大值去对比
for (let key in obj) {
if (obj[key] == max) {
console.log("出现最多的字符是" + key);
console.log("次数是" + max);
}
}
- 闭包是什么
闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
我们来看一段代码
<script>
function fn() {
return function () {};//一个函数
}//创建函数
script>
上面这段代码就是一个闭包,里层函数的作用域连接着外层函数的作用域。
那么什么是闭包关闭了函数的自由变量呢?
我们再来看一段代码:
<script>
function fn() {
let a = 10
return function () {
console.log(a)
};
}
fn()();
script>
正常来说当一个函数执行后,它里面定义的变量会进行垃圾回收机制,变量a会被清理掉,但是在里层函数中我们打印出了a,说明a并没有被回收掉。
所以这就是闭包关闭了函数的自由变量。
- 闭包可以解决什么问题【闭包的优点】
2.1 内部函数可以访问到外部函数的局部变量
2.2 闭包可以解决的问题
我们来看这样一段代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
ul li {
background-color: red;
margin: 10px;
}
style>
<body>
<ul>
<li>1li>
<li>2li>
<li>3li>
<li>4li>
ul>
<script>
var lis = document.querySelectorAll("ul li");
console.log(lis);
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener("click", function () {
alert(i);
});
}
script>
body>
html>
我们随便点击一个li,它最后弹出的数字都为4,但是我们想让它以0,1,2,3它们对应的索引进行展示,该如何做到?我的答案是使用闭包或者let。
我们先来探究一下为什么会产生这种情况,大家都知道js的执行机制是先将同步任务执行完,才会进入事件循环,所以最后i会变为4.
使用闭包解决:
<script>
var lis = document.querySelectorAll("ul li");
console.log(lis);
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].addEventListener("click", function () {
alert(i);
});
})(i);
}
script>
将i注入到一个函数中,i会保存在内存当中,解决。
使用let解决:
<script>
var lis = document.querySelectorAll("ul li");
console.log(lis);
for (let i = 0; i < lis.length; i++) {
lis[i].addEventListener("click", function () {
alert(i);
});
}
script>
由于let会产生独立作用域,所以也能解决这个问题。
- 闭包的缺点
3.1 变量会驻留在内存中,造成内存损耗问题。
3.2 内存泄漏【ie】 ,只有ie浏览器才会产生内存泄漏。
如何解决内存损耗问题:把闭包的函数设置为null
<script>
var lis = document.querySelectorAll("ul li");
console.log(lis);
for (let i = 0; i < lis.length; i++) {
(function () {
lis[i].addEventListener("click", function () {
alert(i);
});
lis[i] = null;
})();
}
script>
1、原型可以解决什么问题?
原型主要用来实现对象共享属性和共享方法
2、谁有原型
函数有prototype
对象有__proto__
我们来看代码演示:
<script>
function fn() {
this.a = "2";
}
fn.prototype.a = "4";
let obj = new fn();
obj.a = "1";
obj.__proto__.a = "3";
Object.prototype.a = "5";
console.log(obj.a); //1
</script>
结果为1
我们将obj.a注释掉
<script>
function fn() {
this.a = "2";
}
fn.prototype.a = "4";
let obj = new fn();
//obj.a = "1";
obj.__proto__.a = "3";
Object.prototype.a = "5";
console.log(obj.a); //2
script>
结果为2
我们将this.a ="2"注释掉
<script>
function fn() {
// this.a = "2";
}
fn.prototype.a = "4";
let obj = new fn();
// obj.a = "1";
obj.__proto__.a = "3";
Object.prototype.a = "5";
console.log(obj.a); //3
script>
结果为3
我们将obj.proto.a = “3”;注释掉
<script>
function fn() {
// this.a = "2";
}
fn.prototype.a = "4";
let obj = new fn();
// obj.a = "1";
// obj.__proto__.a = "3";
Object.prototype.a = "5";
console.log(obj.a); //4
script>
结果为4
我们将 fn.prototype.a = “4”;注释掉
<script>
function fn() {
// this.a = "2";
}
//fn.prototype.a = "4";
let obj = new fn();
// obj.a = "1";
// obj.__proto__.a = "3";
Object.prototype.a = "5";
console.log(obj.a); //5
script>
结果为5
我们将Object.prototype.a = “5”;注释掉
<script>
function fn() {
// this.a = "2";
}
// fn.prototype.a = "4";
let obj = new fn();
// obj.a = "1";
// obj.__proto__.a = "3";
// Object.prototype.a = "5";
console.log(obj.a); //3
script>
结果为undefied,最顶层为null。
查找规则:
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4.1 是什么?:就是把原型串联起来
4.2 原型链的最顶端是null
总的来说原型链就是原型可以实现对象共享属性和共享方法,函数拥有原型prototype,对象拥有原型__proto__,原型中对象查找属性或者方法的顺序为先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找,最后形成一条链,也就是我们说的原型链。
方式一:
ES6中的class
<script>
class Parent {
constructor() {
this.age = 18;
}
}
class Child extends Parent {
constructor() {
super();
this.name = "张三";
}
}
let obj = new Child();
console.log(obj, obj.name, obj.age);
script>
方式二:
原型链继承
<script>
function Parent() {
this.age = 18
}
function Child() {
this.name="张三"
}
Child.prototype = new Parent();
let obj = new Child();
console.log(obj,obj.name,obj.age);
script>
缺点:原型链方案存在的缺点:多个实例对引用类型的操作会被篡改。
方式三:
借用构造函数继承
<script>
function Parent() {
this.age = 18
}
function Child() {
this.name="张三"
Parent.call(this)
}
let obj = new Child();
console.log(obj,obj.name,obj.age);
script>
缺点:
1、只能继承父类的实例属性和方法,不能继承原型属性/方法
2、无法实现复用,每个子类都有父类实例函数的副本,影响性能
方式四:组合式继承
<script>
function Parent() {
this.age = 18
}
function Child() {
this.name="张三"
Parent.call(this)
}
Child.prototype = new Parent();
Child.prototype.constructor = Child; //重新指向自己
let obj = new Child();
console.log(obj,obj.name,obj.age);
script>
缺点:在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。
共同点:功能一致
可以改变this指向
语法:函数.call()、函数.apply()、函数.bind()
函数第一个参数为指向哪里,第二个参数往后都是传入的参数。
区别:
1、 call和apply可以立即执行,bind不会立即执行,因为它返回的是一个函数,需要加入()进行执行
2、参数不同:apply的第二个参数是数组,而call和bind需要一个一个写。
场景:
1、使用apply
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1))
因为Math.max数学对象的参数它不能直接是一个数组 ,给它加上apply之后,就可以直接传入一个数组。
还有一种办法可以直接传入数组,使用扩展运算符将数组进行展开。
2. 用bind的情况
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
head>
<body>
<div id="btn">1div>
<div id="h1s">2div>
<script>
var btn = document.getElementById("btn");
var h1s = document.getElementById("h1s");
btn.onclick = function () {
console.log(this.id);
}.bind(h1s);
script>
body>
html>
原本this应该指向btn,使用bind改变它的指向,最后打印出来的值为hls。
sort0 方法用于对数组的元素进行排序,并返回数组,默认排序顺序是根据字符串 Unicode码点。
语法: array.sort(sortby); 参数 sortby可选,规定排序顺序,必须是函数
注:如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序,要实现这一点,首先应把数组的元素都转换成字符申(如有必要),以便进行比较。
arr.sort0
如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数
a 和 b,其返回值如下若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值 若 a等于 b,则返回 0.
若 a 大于 b,则返回一个大于 0 的值
场景一、
<script>
let arr = [1, 213, 44, "32", "111"];
console.log(arr.sort());
console.log(
arr.sort(function (a, b) {
return a - b;
})
);
script>
<script>
let obj = [
{
name: "张三",
age: 20,
},
{
name: "李四",
age: 18,
},
{
name: "天云",
age: 22,
},
{
name: "云米",
age: 17,
},
];
console.log(
obj.sort(function (a, b) {
return a.age - b.age;
})
);
script>
<script>
let obj = [
{
name: "张三",
age: 20,
},
{
name: "李四",
age: 18,
},
{
name: "天云",
age: 22,
},
{
name: "云米",
age: 17,
},
];
function fn(age) {
return function (a, b) {
let value1 = a[age];
let value2 = b[age];
return value1 - value2;
};
}
console.log(obj.sort(fn("age")));
script>
公共点:在客户端存放数据
区别:
- 数据存放有效期 sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没了】 localStorage : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。 cookie :
只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。- localStorage、sessionStorage不可以设置过期时间 cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
- 存储大小的限制 cookie存储量不能超过4k localStorage、sessionStorage不能超过5M ****根据不同的浏览器存储的大小是不同的。
链接https://blog.csdn.net/Zhang_wang_yun/article/details/130031214