001JS进阶

JS进阶

基本数据类型

数字

var num = 1;

字符串

var str = '';

布尔

var bool = false

undefined

var undf = undefined;

null(数据类型存疑)

var nul = null;

Symbol(es6新增)

// 目的:为避免重复key被覆盖,用于专做属性id标识
// Symbol 不能被 new; 但每一次的值都是new出来的
var sb1 = Symbol()  // Symbol()
var sb2 = Symobl('abc')  // Symbol(abc)
var sb3 = Symobl('abc')  // Symbol(abc)

// 注意:  sb2  !== sb3
let obj = {};
obj[sb2] = 'a';
obj[sb3] = 'b';
// 输出obj为  {Sybol(abc): 'a', Sybol(abc): 'b'}

// 取值
let keys = Object.getOwnPropertySymbols(obj)
for(let key of keys) {
	// obj[key]
}

let obj2 = {abc: 1; abc: 2};
// 输出obj2为  {abc: 2}

// 从缓存池中取原有值
var sb4 = Symbol.for('green')
var sb5 = Symbol.for('green')
//  注意: sb4 === sb5

BigInt(es6新增)

// 解决大数字展示或计算问题
var maxNum = Number.MAX_SAFE_INTEGER  // 最大的安全临界点
var max1 = maxNum + 1;
var max2 = maxNum + 2;
// 注意  max1 === max2

var maxNum_b = BigInt(Number.MAX_SAFE_INTEGER);
var max1_b = maxNum_b + 1n;
var max2_b = maxNum_b + BigInt(2);
// 注意  max1_b !== max2_b

// 对IE支持较差   使用BigInt时,带小数的运算会被取整

类型验证

// 类型区分方式    typeof num

typeof nul // object  (作为object类型的一个bug)

严格模式

过渡产物:限制一些诡异的代码编写,避免bug出现

function fn() {
	// console.log(this, 'this');  // 顶层对象的挂载会直接影响性能  打印this对象
	“use strict”;
	console.log(this, 'this')  // 打印结果为  undefined
}
fn()

引用数据类型

new Date();  // object
new Function();  // function
new Array();  // object

// instanceof 判断实例对象师傅从属于某个构造函数
var date = new Date()

console.log(date instanceof Date);  // true
console.log(date instanceof Object);  // true  单独的使用instanceof判断Object是不准确的

补: 引用数据类型存放在堆空间;

​ 引用数据类型的指向(0x0011)存在在栈空间;

​ 基本数据类型值存放在栈空间;

​ 栈空间还会存放函数执行;

​ 栈: 先进后出;

​ 对象的存储: 一个地址 + 地址指向的数据

创建对象的方式

字面量(直接声明给值)

var obj = {a:1,b:2};

构造函数

var obj2 = new Object();

var tmp = {name: 'green'}
var obj2_1 = new Object(tmp)
// 注意: 此处  tmp === obj2_1   
// Object构造函数如果传入是引用类型的值,任然返回这个值,经他们复制的变量保有和源对象相同的引用地址
// 如果传递为null或者undefined返回空对象
// 基本数据类型返回其包装类型

依托原型创建对象(模板)

 Object.create(原型,描述符)
var obj3 = Object.create({company: '111'},{
	age: {
		
	}
})
初始值 和 是否可写
var obj3_1 = Object.create({},{
	age: {
		value: '36',  // 初始值
		writable: false,  // 不可写
	}
})
设置获取方式
var globalAgee;
var obj3_2 = Object.create({},{
	age: {
		// 数据劫持
		set(newAge) {
			if(newAge > 100 || newAge < 1) return;
			globalAgee = newAge;
			console.log('age被设置了');
		},
		get() {
			console.log('age被获取了');
			return: globalAgee;
		}
	}
})
设置级别
是否可以被for in 枚举
var obj3_3_1 = Object.create({},{
	age: {
		enumberable: true,  // 默认false
		get() {
			console.log('第一次设置属性的get');
		}
	}
})

// 只有在enumberable: true, 才会被执行
// for in 迭代 key ;  for of 迭代 value
for( var key in obj3_3_1 ) {
	console.log('key: ', key, 'val: ': obj3_3_1[key] )
}
是否可以被修改配置(删除属性 或 再次配置)
var obj3_3_2 = Object.create({},{
	age: {
		configurable: true,  // 默认false
		get() {
			console.log('第一次设置属性的get');
			return 36;
		}
	}
})

// 删除这个属性  只有 configurable: true 才可以删除
delete obj3_3_2.age  

// 允许再次设置  只有 configurable: true 才可以操作
// Object.defineProperty(对象,属性名,描述符)
Object.defineProperty(obj3_3_2, 'age', {
	get() {
		console.log('第二次设置get')
		return: '2222'
	}
})

合并对象返回新对象

// assign 属性的附加
var obj4 = Object.assign({b:3},{a:1,d:4})

数组方法

创建方式

var arr = new Array();

var arr2 = [1,2]

声明数组初始长度

var arr3 = new Array(100)
// 长度为100, 但值为空 empty ,且无法遍历  (以下代码无法执行)
var newArr3 = arrs.map(function(){
	console.log('map被执行');
	return 111;
})

// 若想执行 arr3_1输出为1-100的数组集合
var arr3_1 = new Array(100).fill(undefined).map(function(item, index){
	console.log('map被执行');
	return index + 1;
})

var arr = [1,2,3,4]
// 数据新增可以跳过增加; 数组为一块连续的空间
arr[10] = '值11'

// 在2后插入abc
arr.splice(2, 0, 'a', 'b', 'c')  // 从2开始删除0个再插入abc

// 最前方添加元素
arr.unshift(0)

// 最后方添加元素
arr.push(100)

var arr = [1,2,3,4]
// splice(开始索引, 删除几个, ...添加的元素)
arr.splice(3, 1)

var arr = [1,2,3,4]

arr[2] = '第三个值'

// 修改第二个值为 二
arr.splice(1, 1, '二')  // 从2开始删除0个再插入abc

var arr = [1,2,3,4,5,6,7,8,9,10]
// 查单一
console.log(arr[2])

// 查最初
console.log(arr.shift())

// 查最后
console.log(arr.pop())

// 查多个 索引2-5之间
console.log(arr.slice(2,5))

// 查找元素
// find(元素, 索引, 原数组)
var findEle = arr.find(function(ele, index, arr){
	return ele === 10;
})
if(findEle !== undefined) {
	console.log(findEle)
}

// 查找索引
// findIndex(元素, 索引, 原数组)
var findIndex = arr.findIndex(function(ele, index, arr){
	return ele === 10;
})
if(findIndex !== -1) {
	console.log(findIndex)
}

// 遍历(执行次数为数组长度length)
// forEach(元素, 索引, 原数组)  遍历完所有
arr.forEach(function(ele, index, arr){
	console.log(ele)
})

// map(元素, 索引, 原数组)  返回[新元素+新数组] 遍历完所有
var newArr = arr.map(function(ele, index, arr){
	// 返回所有元素
	return {
		num: ele
	}
})

// filer(元素, 索引, 原数组) 过滤  遍历完所有
var odd = arr.filer(function(ele, index, arr){
	// 根据满足条件返回当前元素
	return ele % 2 === 0;
})

// 判断(出现某些条件后,结束循环, 执行次数 <= 数组长度length)
// some(元素, 索引, 原数组)  找true的循环优化
var isOk1 = arr.some(function(ele, index, arr){
	// 查找元素中有没有数字
	return typeof ele === 'number';
})

// every(元素, 索引, 原数组)  找false的循环优化
var isOk2 = arr.every(function(ele, index, arr){
	// 查找元素中是不是不存在字符串
	return typeof ele !== 'string';
})

// 求和
// reduce
var sum = arr.reduce(function(sum, ele) {
	return sum + ele;
},0)

// 当不传递第二个参数0时, sum及ele第一轮没有return之前参数分别为n1,n2
var sum1 = arr.reduce(function(sum, ele) {
	return sum + ele;
})

补: 栈和队列

​ 队列(先进先出)排队 unshift前入 shift前出

​ 栈(后进先出)套娃 push后入 pop后出

预解析

变量的声明 会存放在最顶部

函数表达式与函数声明区别

​ 函数声明: function fn1() {} 提前声明(包含其值)

​ 函数表达式:var fn2 = function() {} 提前声明(没有赋值)

console.log(fn1, fn2);
function fn1() {};
var fn2 = function() {};

//log 输出为  fn1(){}  undefined
// 等同于

function fu1() {};
var fn2;
console.log(fn1, fn2);
fn2 = function() {};

作用域

就近原则逐级向上查找

// 匿名自执行函数
(function(){})()
// 等同于 var fn = function(){};  (fn)();
var a,b;

(function(){
	console.log(a)  // undefined
	var a = 1;
	console.log(a)  // 1
})()

// 等同于
// (function(){
//	var a;
//	console.log(a)  // undefined
//	a = 1;
//	console.log(a)  // 1
// })()

console.log(a)  // undefined
var a,b;

(function(){
	console.log(b)  // undefined  此处b为外部a; 没有存在声明提升
	b = 2;
	console.log(b)  // 2
})()

console.log(b)  // 2

传值传引用

基本数据类型传值; 引用数据类型传地址 地址可以对数据发生改变

function fn(n, arr) {
	n = 999;
	console.log(n);  // 999
	arr[0] = 100;
	console.log(arr);  // [100, 2]
}

var n1 = 1;  // 基本数据类型
var arr1 = [1,2];  // 引用数据类型

fn(n1, arr1);

console.log(n1);  // 1
console.log(arr1);  // [100, 2]

深浅拷贝

浅拷贝

var arr = [1,2];
arr2 = arr;
arr[0] = 111;
arr2[1] = 222;

// arr === arr2
// arr 与 arr2 具有相同的指向/引用/地址
// 引用/对象之间 === 对比的是对象的内存地址, 而不是具体的数值

深拷贝

解决 引用数据类型 相同地址x修改造成的相互影响问题

var arr = [1,2];
var arr2 = arr.slice(); // 返回新数组,内部元素复用
// arr !== arr2
// arr 与 arr2 不具有相同的地址

// 元素为引用数据类型
var arr3 = [{a:1}, {a: 2}]
var arr4 = arr3.slice(); 
// arr3 !== arr4
// arr3 与 arr4 不具有相同的地址

// arr3[0] === arr4[0]
// arr3[0] 与 arr4[0]  具有相同的地址


var arr5 = [{a:1}, {a: 2}]
var arr6 = arr3.slice(); 

arr5[0] = 'xxx';
// 将arr5[0] 引用数据类型赋值为一个基本数据类型
// 不会改变arr6[0] 的指向  arr[0] 还是指向原有的对象{a:1}

手写实现深拷贝

let obj1 = {
	arr: [1,2,3],
	arrOfObjs: [{c: 5}, {d: 6}],
	obj: {val: 4},
	fn: function() {
		return 5;
	},
	nul: null,
	undef: undefined,
};

var cloned = deepClone(obj1);

function deepClone(obj) {
	// 处理基本的数据类型 undefined null function
	if(typeof obj !== 'object' || !obj){
		retutn obj;
	}
	
	// 获取类型
	// obj不是方法的参数, 改变的this, 由于不同类型的对象的type不同, toString从不同this拿到的就不一样
	var type = Object.prototype.toString.call(obj);
	
	// 处理引用数据类型  数组 || 对象
	if(typeof obj === 'object') {
		if(obj instanceof Array) {
			// 数组
			// var tmp = new Array();
			// obj.constructor() 得到的将会是这个new的实例  === Array()
			var tmp = new obj.constructor();
			for(var key in obj){
				tmp[key] = deepClone(obj[key]);
			}
			return tmp;
		}else {
			// 对象
			if (type === '[object Object]'){
				// 纯object
                var tmp = new Object();
                for(var key in obj){
                    tmp[key] = deepClone(obj[key]);
                }
                return tmp;
			}else {
				// 其他数据类型
			}
		}
	}
}
JSON处理 深拷贝
var cloned = JSON.parse(JSON.stringify(obj1))
// 此时fn: function() {return 5;}, 丢失
// JSON方式不会处理非JSON格式的特殊数据

构造函数

函数和构造函数都可以被new

构造函数约定首字母大写

  • new 返回内部的this

  • 调用返回return返回的值

// 普通函数
function fn() {}

// 构造函数
function Person() {
	// 非严格模式下, this是window
	// console.log(this) // window对象
	// 严格模式下, this是undefined
	// "use strict"
	// console.log(this) // undefined
	
	// 在new的过程中,就是当前对象的实例 
	// this.constructor === Person === arguments.callee
	// this instanceof arguments.callee

	// 在new的过程中 构造函数内部会声明一个 var this = {};  renturn 的对象是这个 this
	this.name = 'name';
	return '调用返回值';
}

var invoke = Person();  // invoke ==> 调用返回值  执行的函数体内的代码

var instance = new Person(); // instance ==> Person{name: 'name'}  返回的是this对象

原型

原型更多存储原型方法,让所有实例复用

原型也可以存储不改的属性或者全局的属性 用于原型间的数据共享

function Animal(name) {
	// this
	this.name = name;
	// N个对象 N个相同方法赋值给N个属性
	// this.eat = function() {
	// 	 console.log('eat...')
	// }
}

// 原型可提供所有实例进行访问
// 1、隐式原型  实例.__proto__
// 2、显示原型  构造函数.prototype
Animal.prototype.eat = function() {
	console.log(this.name + 'eat...')
}
// 数据共享
Animal.prototype.age = 123;

// Animal.prototype === an1.__proto__  === an2.__proto__


var an1 = new Animal('小红');
an1.eat();
console.log(an1.name);

var an2 = new Animal('小黄');
an2.eat();
console.log(an2.name);

// 遍历 可获取到所有的属性 (包含原型和自身)
for(var key in an1){}

// 属性判断  in
// 属性判断无法区分 属性是否是原型属性 还是实例自身的属性
console.log('name' in an1);  // true
console.log('age' in an1);  // true

// 判断是否属于自身属性  
// 1、hasOwnProperty
consloe.log(an1.hasOwnProperty('name')) // true
consloe.log(an1.hasOwnProperty('age')) // false
// 2、获取key
consloe.log(Object.keys(an1))  // ['name']  默认无法获取原型上的属性
// 3、获取value
consloe.log(Object.values(an1))  // ['小红']  默认无法获取原型上的属性

this指向

函数直接调用

functon fn() {
	console.log(this)  // Window
}
fn()

// 严格模式
"use strict"
function fn1() {
	console.log(this)  // undefined
}
fn1()

函数存储在数组中调用

functon fn() {
	console.log(this)  // arr
}

var arr = [fn, 2, 3]
arr[0]()
// 谁.调用的方法, this就指向谁
// arr[0]  ==> arr.0  取到的是函数()  this 指向数组

函数存储在对象中调用

functon fn() {
	console.log(this)  // obj
}

var obj = {fn: fn}
obj.fn()
// 谁.调用的方法, this就指向谁

联合调用

functon fn() {
	console.log(this)  // obj.a
}

var obj = {a: {fn: fn}}
obj.a.fn()
// 谁.调用的方法, this就指向谁

改变this指向

通过外部变量改变this指向
var obj = {
	name: 'zhangsan',
	init: function() {
		console.log(this)  // obj
		var that = this // 函数执行上下文, 访问外部变量
		var $btn = document.querySelector('#btn')
		$btn.onclick = function() {
			console.log(that)  // obj
		}
	}
}

obj.init()
call改变this指向
var str = 'this'

function fn() {
	console.log(this) // String{'this'}   str的包装对象
}

// call 调用的时候第一个参数作为this 后面参数作为传入参数
// 第一参数4种情况
// 1、不传或null或undefined  this指向window
// 2、传递顶一个函数的函数名, this指向函数引用
// 3、传递字符串、数值或布尔值  this指向其对应的包装对象 String Number Boolean
// 4、传递对象,this指向这个对象
fn.call(str,1,2,3)
apply改变this指向

劫持另外一个对象的方法,继承另外一个对象的属性

var str = 'this'

function fn() {
	console.log(this) // String{'this'}   str的包装对象
}
// apply 第一个参数作为this 后面参数为一个数组作为传入参数
fn.apply(str,[1,2,3])
bind改变this指向
var bool = false

function fn() {
	console.log(this) // Boolean{false}   bool的包装对象
}
// bind 返回一个改变this指向的函数, 也可以提前实现参数传递
var newFn = fn.bind(bool,97)
newFn(98,99)

箭头函数this指向

var arrows = () => {
	console.log(this)  // window
}

arrows()

箭头函数中this指向不受call apply bind 影响

原型链

构造函数指向

// 构造函数
function Person() {}

// 通过构造函数创建实例
var p1 = new Person()

// 实例可以通过 constructor 找到构造器
// p1.constructor === Person  // true

// 实例通过隐式的__proto__找到原型,获取共享的数据
// 1、in 查询实例或原型上
// 'constructor' in p1  // true
// 2、hasOwnProperty 查询实例上
// p1.hasOwnProperty('constructor')  // false
// 3、对象.__proto__ === 方法.prototype
// p1.__proto__ === Person.prototype // true

关于Object与构造函数间的关系

// 构造函数
function Person() {}

// function Object() {}
var obj = new Object();

// 对象.__proto__ === 方法.prototype
// 隐式原型 === 显示原型
// obj.__proto__ === Object.prototype // true

// 构造函数所有原型的上级都是Object的原型
// Object.prototype === Person.prototype.__proto__

// 原型链的终点
// Object.prototype.__proto__ === null

关于Function

var fn = new Function();

// 构造函数所有原型的上级都是Object的原型
// Object.prototype === Function.prototype.__proto__

// Object
// 1、Object.constructor
// Object作为构造函数, 它的构造器  Object 是通过 new Function() 创建的
// 所以.constructor指向 Function

// 2、Object.prototype.constructor
// {} 或者 new Object() 的实例, 通过原型.constructor 访问到自己的构造器

// Function.constructor === Function // true

原型继承

属性共享, 但容易被改变,影响所有

继承方式:你的原型赋值给我的原型

function Animal() {};
Animal.prototype.desc = '这是个动物'
Animal.prototype.eat = function() {
	console.log(this.name, '吃....', this.desc);
}

function Cat(name) {
	this.name = name;
}

function Dog(name) {
	this.name = name;
}

// 原型继承: 你的原型赋值给我的原型
Cat.prototype = Animal.prototype;
Dog.prototype = Animal.prototype;

new Cat('猫').eat();  // 猫 吃.... 这是个动物
new Dog('狗').eat();  // 狗 吃.... 这是个动物
function Animal() {};
Animal.prototype.desc = '这是个动物'
Animal.prototype.eat = function() {
	console.log(this.name, '吃....', this.desc);
}

function Cat(name) {
	this.name = name;
}

function Dog(name) {
	this.name = name;
}

// 原型继承: 你的原型赋值给我的原型
Cat.prototype = Animal.prototype;
Dog.prototype = Animal.prototype;

// 对原型进行修改
Cat.prototype.desc = '这个一个猫';

new Cat('猫').eat();  // 猫 吃.... 这个一个猫
new Dog('狗').eat();  // 狗 吃.... 这个一个猫
function Animal() {};
Animal.prototype.desc = '这是个动物'
Animal.prototype.eat = function() {
	console.log(this.name, '吃....', this.desc);
}

function Cat(name) {
	this.name = name;
}

function Dog(name) {
	this.name = name;
}

// 原型继承: 你的原型赋值给我的原型
Cat.prototype = Animal.prototype;

new Cat('猫').eat();  // 猫 吃.... 这是个动物

Dog.prototype = Animal.prototype;

// 对原型进行修改
Cat.prototype.desc = '这个一个猫';
Cat.prototype.eat = function() {
	console.log(this.name, '吃吃吃....', this.desc)
};

new Dog('狗').eat();  // 狗 吃吃吃.... 这个一个猫

构造函数继承

// 利用父类构造函数实现数据共享
function Animal(name) {
	this.name = name;
}
Animal.prototype.eat = function() {
	console.log(this.name, '吃....');
}

// 从子类中调用父类构造函数
function Cat(name) {
	Animal.call(this, name);
}

new Cat('猫').eat();

组合继承

白嫖构造函数 + 偷取他的原型

function Animal(name) {
	this.name = name;
	this.eat = funtion() {
		console.log(this.name, '吃...');
	}
}

function Cat(name) {
	Animal.call(this, name);
}
Cat.prototype = Animal.prototype;

new Cat('猫').eat();

组合派生继承

function Animal(name) {
	this.name = name;
	this.eat = funtion() {
		console.log(this.name, '吃...');
	}
}

function Cat(name) {
	Animal.call(this, name);
}
// 1、解决公共原型被修改影响所有实例的问题
Cat.prototype = Object.create(Animal.prototype);

var c1 = new Cat('猫').eat();  // c1.constructor  ==> Animal
function Animal(name) {
	this.name = name;
	this.eat = funtion() {
		console.log(this.name, '吃...');
	}
}

function Cat(name) {
	Animal.call(this, name);
}
Cat.prototype = Object.create(Animal.prototype);
// 2、掰回歪曲的构造函数
Cat.prototype.constructor = Cat;
// 3修改原型的[类型描述]
Cat.prototype[Symbol.toStringTag] = Cat.name;

var c1 = new Cat('猫').eat();  // c1.constructor  ==> Cat

闭包

函数内部访问外部的变量,形成了闭包

目的: 需要使用内部的变量, 避免全局变量的污染

最终 返回出来的函数,引用外部变量的这个闭包,最终可以置为null来释放变量和闭包的空间

function fn() {
	var b = new Array(200000).fill('');
	function inner() {
		var innerB = b; // 形成闭包
	}
	inner(); // 闭包会随着函数的结束而消失
	
	return inner;  // 向外部返回
}

var closureB = fn();
// 使用完毕后释放内存
closureB = null;

递归

函数自己调用自己

递归的场景是逻辑复杂的场景,调用自身函数

所有递归可以实现的循环都能实现

递归由于压栈性能略差,但代码可读性较高

// 防止栈内存溢出
// 1、设置递归的执行条件
var i1 = 0;
function fn1() {
	console.log('方法执行111', i1++);
	if(i1 < 10) {
		fn1();
	}
}

fn1();

// 2、设置递归退出条件
var i2 = 0;
function fn2() {
	console.log('方法执行222', i2++);
	if(i2 > 10) {
		return;
	}
}
fn2();

函数柯里化

实例数据功能独立

把多个参数的函数变成接受一个单一参数

高阶函数:一个函数 return 一个函数

function calc() {
	// 缓存参数
	// 第一次参数的缓存 calc(参数)
	var slice = Array.prototype.slice
    // 缓存所有参数的总数组
	var args = slice.call(arguments);
	// 闭包
	function inner() {
        // calc(参数) => 结果再调用的参数
    	var innerArgs = slice.call(arguments);
    	// 数组拼接
    	args = args.concat(innerArgs);
    	return inner;
  	}
	// 给inner添加方法  inner ==> 变量对象
	inner.calcCount = function () {
    	return args.reduce(function (sum, n) {
     		return sum + n;
    	}, 0);
	};
	// 高阶函数
	return inner; // 方法
}

var xh = calc(99);
var xhtotal = xh(88)(98, 66);
console.log(xhtotal.calcCount());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值