注:链接大部分是笔记的参考文章,想详细了解的可以点进去看。
*Javascript (一):封装
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){alert("吃鱼")};
let cat1 = new Cat("小黄","black");
let cat2 = new Cat("锋锋","white");
cat1.eat();
*Javascript(二):构造函数的继承
//动物类
function Animal(){
this.species = "动物";
}
//猫类
function Cat(name,color){
this.name = name;
this.color = color;
}
//如何让猫继承动物呢?
一、 构造函数绑定
使用call或apply方法
function Cat(name,color){
Animal.apply(this,arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat;
alert("cat1.species");//动物
二、 prototype模式
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat;
alert("cat1.species");//动物
任何一个prototype对象都有一个constructor属性,指向它的构造函数. 当执行了Cat.protoType = new Animal 这句代码后,Cat.protoType.constructor指向Animal 而实例car1的cat1.constructor也指向Animal
因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。
为了避免继承链紊乱,下文都遵循这一点,即如果替换了prototype对象,
o.prototype = new plant();
o.prototype.constructor = o;
三、直接继承prototype
function Animal(){}
Animal.prototype.species = "动物";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
虽然这种继承方法相较第二种效率更高更省内存(不用建立Animal实例),但此时,Animal.protoType.constructor也指向了Cat。这种方法其实是错误的。
四、利用空对象作为中介
function F(){
}
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
alert(Animal.prototype.constructor);//Animal
这样F()几乎不占内存,且修改Cat的prototype.constructor不会影响到Animal
将上面的方法封装一下,以便调用
function extend(Child,Parent){
function F(){}
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototypr.constructor = Child;
Child.uber = Parent.prototype;
}
然后就可以调用啦
extend(Cat,Animal);
let cat1 = new Cat("锋锋","黑色");
alert(cat1.species);//动物
五、拷贝继承
function Animal(){
}
Animal.prototype.species = "动物";
function extend2(Child,Parent){
var c = Child.prototype;
var p = Parent.prototype;
for(var i in p){
c[i]=p[i];
}
c.uber = p;
}
extend2(Cat,Animal);
let cat1 = new Cat("锋锋","黑色");
alert(cat1.species);//动物
*Javascript(三):非构造函数的继承
非构造函数的继承,比如:对象间的继承
var Chinese = {nation:'中国'}
var Doctor = {career:'医生'}
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var Doctor = object(Chinese);
//Doctor.career = '医生';
alert(Doctor.nation);
一、内置类型
NaN表示未定义,或不可表示的值 boolean ->true/ false 函数就是对象
二、Type of
typeof 对于基本类型,除了 null 都可以显示正确的类型 对于 null 来说,虽然它是基本类型,但是会显示 object,这是一个存在很久了的 Bug
三、类型转换
3.1 boolean
在条件判断时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。(不懂)
3.2对象转基本类型
对象在转换基本类型时,首先会调用 valueOf 然后调用 toString。这两个方法可以重写。 (先赋值,后类型转换)
let a = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive]() {
return 2;
}
}
1 + a // => 3
'1' + a // => '12'
3.3 四则运算符
当数字和字符串进行运算时:如: '2'和4 加法运算:字符串优先级高。其它运算,数字优先级高。
'2'+4='24'
'2'*4 =8
[1, 2] + [2, 1] // '1,22,1'
'a' + + 'b' // -> "aNaN"
因为: + '1' -> 1
四、原型
构造函数 指向 就是“===”(完全等于) prototype上的所有属性,子孙都可以共享
function Foo() {}
const f1 = new Foo();
f1.__proto__ === Foo.prototype; // 实例(子)的__proto__ === 其构造函数(父)的原型(prototype)
Foo.__proto__ === Function.prototype;
Foo.prototype.constructor ===Foo();
五、instanceof(实例)
》f1 instanceof Foo
》true
六、this
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html 谁调用this指向谁
七、闭包
【详情见:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html】 一般情况下:
function f1(){
var n=100;
function f2(){
alert(n); //100
}
}
一般情况下,内部函数可以读取全局变量,但是外部函数无法读取内部变量。
但是!!!在f1函数return内部f2函数!!就可以在f1外部读取到内部变量了!!
闭包的目的:能够读取其他函数内部变量的函数 在下面代码中,f2函数就是闭包
function f1(){
let n=100;
function f2(){
alert(n);
}
return f2();
}
let result = f1();
result(); //100
闭包的用途
闭包的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
看下面的例子:
function f1(){
let n=100;
add=function(){n+=1;}
funciton f2(){
alert(n);
}
retrun f2();
}
let result = f1();
result(); //100
add();
result();//101
result运行了两次。一次值为100,第二次值为101,这证明了函数f1中的局部变量n一直保存在内存中,没有在f1调用后被自动清除。
使用闭包的注意点
-
由于闭包会使函数中的变量都被保存在内存中,所以不能滥用闭包。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
-
闭包会在父函数外部,改变父函数内部变量。所以一定要小心,不要随便改变父函数内部变量的值。
思考题
例子1:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
例子2:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
例子1 输出结果是:The Window 例子2 输出结果是:My Object
this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。
例子1中alert(object.getNameFunc()());这句调用的时候在window中,所以this指向window
八、深浅拷贝
浅拷贝
var Chinese={nation:'中国'};
var Doctor ={career:'医生'};
function extendcopy(p){
var c={};
for( var i in p){
c[i]=p[i]
}
c.uber=p;
return c;
}
Chinese.birthPlace=['广州','四川','上海'];
var Doctor = extendcopy(Chinese);
Doctor.birthPlace.push('厦门');
alert(Chinese.birthPlace);//广州,四川,上海,厦门
当父对象属性为数组时,子对象改变,会将父对象的属性改变。 extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做"浅拷贝"
深拷贝
function deepcopy(p,c){
var c = c ||{} //如果不传入c,则赋c为{
for( var i in p){
if(typeof p[i] === 'object' ){
c[i] = (p[i].construcor===array)?[]:{};
deepcopy(p,c);
}else{
c[i]=p[i];
}
}
return c;
}
这样写,父对象就不会受到影响了。
九、call、apply、bind使用和区别
https://juejin.im/post/5a9640335188257a7924d5ef
首先,它们的作用是什么呢? 答案是:改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。
let obj = {name: 'tony'};
function Child(name){
this.name = name;
}
Child.prototype = {
constructor: Child,
showName: function(){
console.log(this.name);
}
}
var child = new Child('thomas');
child.showName(); // thomas
// call,apply,bind使用
child.showName.call(obj);
child.showName.apply(obj);
let bind = child.showName.bind(obj); // 返回一个函数
bind(); // tony
- call、apply与bind的差别
call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。
- call、apply的区别
他们俩之间的差别在于参数的区别,call和aplly的第一个参数都是要改变上下文的对象。 除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。
let arr1 = [1, 2, 19, 6];
//例子:求数组中的最值
console.log(Math.max.call(null, 1,2,19,6)); // 19
console.log(Math.max.call(null, arr1)); // NaN
console.log(Math.max.apply(null, arr1)); // 19 直接可以用arr1传递进去
主要应用: 1、将伪数组转化为数组(含有length属性的对象,dom节点, 函数的参数arguments)
case1: dom节点:
<div class="div1">1</div>
<div class="div1">2</div>
<div class="div1">3</div>
let div = document.getElementsByTagName('div');
console.log(div); // HTMLCollection(3) [div.div1, div.div1, div.div1] 里面包含length属性
let arr2 = Array.prototype.slice.call(div);//Array.prototype.slice,浅拷贝
console.log(arr2); // 数组 [div.div1, div.div1, div.div1]
十、Map、FlatMap 和 Reduce
Map
语法:var new_array = arr .map(function callback(currentValue [,index [,array]]){ //返回new_array的元素 } [, thisArg ])
- 参数:currentValue 必选,当前元素
index 可选,索引 arr 可选,原数组
[1, 2, 3].map((v) => v + 1)
// -> [2, 3, 4]
FlatMap
语法:var new_array = arr .flatMap(function callback(currentValue [,index [,array]]){ //返回new_array的元素 } [, thisArg ])
Map和FlatMap的区别
区别1:
arr1.map( x => [x * 2] );
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2] );
// [2, 4, 6, 8]
区别2:(flatMap原数组将维)
[1, [2], 3].flatMap((v) => v + 1)
// -> [2, 3, 4]
Reduce Reduce 作用是数组中的值组合起来,最终得到一个值
function a() {
console.log(1);
}
function b() {
console.log(2);
}
[a, b].reduce((a, b) => a(b()))
// -> 2 1
十一、async 和 await
async是异步的意思。 一个函数如果加上 async ,那么该函数就会返回一个 Promise
async function test() {
return "1";
}
console.log(test()); // -> Promise {<resolved>: "1"}
await只能用在async函数中,就是用于等待异步完成
function sleep() {
return new Promise(resolve => {
setTimeout(() => {
console.log('finish')
resolve("sleep");
}, 2000);
});
}
async function test() {
let value = await sleep();
console.log("object");
}
test()
在上面代码中会先打印‘finish’再打印‘object’,因为await会等待sleep()执行完。
async和await相比Promise的优缺点:
优点:处理 then 的调用链,能够更清晰准确的写出代码。
缺点:滥用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。
来源:oschina
链接:https://my.oschina.net/u/4403835/blog/3640645