ES5——类的本质、新增方法、函数、递归、深拷贝和浅拷贝

1.类的本质

class(类)的本质是function,可以简单的认为 类是构造函数的另一种写法

  1. 类有原型对象prototype
  2. 类的原型对象prototype中有constructor指向类本身
  3. 类可以通过原型对象添加方法
  4. 类创建的实例对象有__proto__,指向类的原型对象

ES6的类的绝大部分功能ES5都能做到,新的class写法只是让对象原型的写法更清晰、更像面向对象编程的语法而已

ES6的类就是语法糖

语法糖:是指一种便捷方法,简单理解,有两种方法可以实现同样的功能,但一种写法更加清晰、方便,那这个方法就是语法糖

2. ES5新增方法
  • 数组方法
    迭代(遍历)方法: forEach()map()filter()some()every()

forEach()
语法: array.forEach(function (currentValue, index, arr){})
currentValue 数组当前项的值
index 数组当前项的索引
arr 数组对象本身

// 求和示例
var arr = [1, 2, 3]
var sum = 0
arr.forEach(function (val, index, arr) {
    sum += val
})
console.log(sum);

filter()
语法:array.filter(function(currentValue, index, arr){})
currentValue 数组当前项的值
index 数组当前项的索引
arr 数组对象本身

创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组

直接返回一个新数组

// 筛选数组元素示例
var arr = [66, 5, 12, 7, 29]
// 因为filter返回一个新数组,所以可以直接先声明变量去接收
var newArr = arr.filter(function (val, index, arr) {
   // return val >= 12  // 筛选>=12的元素
   // 筛选偶数值  (%-->取余)
   return val % 2 === 0
})
console.log(newArr);

some()
语法: array.some(function (currentValue, index, arr) {})
currentValue 数组当前项的值
index 数组当前项的索引
arr 数组对象本身

用于检测数组中的元素是否满足指定条件,及查找数组中是否有满足条件的元素

返回布尔值,如果能找到就为true,找不到返回false

如果找到第一个满足条件的元素,则终止循环,不再继续查找

var arr = ['red', 'pink', 'blue']
var flag = arr.some(function (val, index, arr) {
    return val === 'pink'
})
console.log(flag);

forEach和some的区别
forEach(filter也同样) 会一直遍历,即使有return也不会终止迭代
some在return true 时会停止遍历,效率更高

当想要在数组中查询唯一元素,用some更合适

  • 字符串方法
    trim()
    从一个字符串两端删除空白字符
    语法:str.trim()
    不影响原字符串本身,返回一个新字符串
var str = '    星星    '
var newStr = str.trim()
console.log(newStr);
  • 对象方法
    object.defineProperty()
    定义对象中的新属性或修改原有属性
    语法: Object.defineProperty(obj, prop, descriptor)
    obj:必需。目标对象
    prop:必需。需定义或修改的属性的名字
    discriptor:必需。目标属性所拥有的特性
    discriptor说明:以对象形式{}书写
    value 设置属性的值,默认为undefined,不写的情况下为对象中的默认值
    writable 值是否可重写,true|false,默认值为false
    enumerable 目标属性是否可以被枚举,true|false,默认值为false
    configurable 目标属性是否可以被删除或是否可以再次修改特性(该属性的特性(即 configurable的值)),true|false,默认值为false
var obj = {
   id: 1,
    name: '小米',
    price: 9999
}
Object.defineProperty(obj, 'address', {
    value: '北京',
    // writable 值为false 则不允许修改这个属性值 默认值为false
    writable: false,
    // enumerable 值为false则不允许遍历 默认值为false
    enumerable: false,
    // configurable 如果值为false 则不允许删除这个属性 也不允许修改该属性的特性(即 configurable的值) 默认为false
    configurable: false
})
// 遍历对象
console.log(Object.keys(obj));
// 删除对象中的属性
delete obj.address
console.log(obj);
3. 函数进阶
  • 函数的定义和调用
    定义方式
    1>函数声明方式function关键字(命名函数)
    function fn () { }
    2>函数表达式(匿名函数)
    var fn = function () { }
    3>new Function方式
    Function中的参数必须是字符串格式
    new Function('参数1', '参数2'..., '函数体')
    var f = new Function('console.log(123)'); f();
    var f = new Function('a', 'b', 'console.log(a+b)'); f(1, 2);

所有函数都是Function的实例(对象)
函数也属于对象(函数、字符串、数组。。。万物皆对象)

函数
调用方式
1>普通函数
function fn () {console.log(123)}
fn() | fn.call()
2>对象的方法

var o = {
	sayHi: function () {
		console.log(123)
	}
}
o.sayHi()

3>构造函数
function Star () {}
Star()
4>绑定事件函数
btn.onclick = function () {}
点击按钮就可以调用
5>定时器函数
setInterval(function () {}, 1000)
每隔1s调用一次
6>立即执行函数

// 立即执行函数是自动调用
(function () {
	console.log(123)
})()
  • this
    this的指向在函数调用时确定,调用方式不同,决定了this指向不同,一般指向调用者
    this指向

处理函数内部this指向的方法
1> call()
调用一个对象,即调用函数的方式,以及改变函数的this指向
主要作用可以实现继承
fun.call(thisArg, arg1, arg2, ...)

var o = { name: 'andy' }
function fn (a, b) {
	console.log(this)
	console.log(a + b)
}
fn.call(o, 1, 2)
// o->this指向

2> apply()
调用函数且改变this指向
fun.apply(thisArg, [argsArray])
thisArg 在函数运行时指定的this值
argsArray 传递的参数必须是**数组(伪数组)**形式
返回值就是函数的返回值,因为它就是调用函数

var o = { name: 'andy' }
function fn (arr) {
	console.log(this)
	console.log(arr) // 暂未成功人士
}
fn.apply(o, ['暂未成功人士'])

主要应用:
如,可以利用apply借助于数学内置对象求最大值

var arr = [1, 66, 5, 99]
var max = Math.max.apply(Math, arr)
console.log(max)

3> bind()
不会调用函数,但是能改变函数内部的this指向
fun.bind(thisArg, arg1, arg2,...)
thisArg 在函数运行时指定的this值(this指向)
arg1, arg2 传递的其他参数

返回由指定的this值和初始化参数改造的原函数拷贝(原函数改变this之后产生的新函数)

var o = { name: 'andy' }
function fn () {
	console.log(this)
}
var f = fn.find(o)
f()

如果有的函数不需要立即调用,但是又想改变函数内部的this指向时,用bind方法

应用
一个按钮,点击之后禁用,3s后开启

<button>点击</button>

var btn = document.querySelector('button')
btn.onclick = function () {
	this.disabled = true
	// var that = this
	setTimeout(function () {
		// that.disabled = false // 定时器函数中的this指向window
		this.disabled = false
	}.bind(this), 3000)
}

如果同时需要用到本身的this和改变指向的this时,可以先传递当前的this,再传递要修改为的this指向
this.remove[i].onclick = this.delTab.bind(this.remove[i], this)
方法对比

  • 严格模式
    在严格条件下,执行js代码
    严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略
    严格模式对正常js的语义做了一些更改:
    1> 消除js语法的一些不合理、不严谨之处,减少了一些怪异行为
    2> 消除代码运行的一些不安全之处,保证代码运行的安全
    3> 提高编译器效率,增加运行速度
    4> 禁用了ECMAScript未来版本可能会定义的语法,为未来新版本的JavaScript做好铺垫,如一些保留字:classenumexportextendsimportsuper不能做变量名

严格模式可以 应用到整个脚本个别函数中,使用时,可以分为为脚本开启严格模式为函数开启严格模式
1> 为脚本开启严格模式
在所有语句之前放一个特定语句"use strict";'use strict';

<script>
	'use strict';
	// 下面的代码会按照严格模式执行
</script>
// 或 使用立即执行函数时 开启严格模式
<script>
	(function () {
		'use strict';
	})()
</script>

2> 为某个函数开启严格模式

// 此时只为 fn 函数开启了严格模式
function fn () {
	'use strict';
}

严格模式中的变化
1> 变量规定
正常模式中,如果变量没声明就赋值,默认是全局变量;严格模式禁止这种用法,变量必须都先用var命令声明,然后再使用
严禁删除已声明变量。例,delete x;语法是错误的
2> this 指向
全局作用域函数中的this指向window对象
严格模式下全局作用域中的函数的thisundefined
正常模式中构造函数不加new也可以作为普通函数调用,this指向全局
严格模式下,如果构造函数不加new调用,this指向undefined,如果给它赋值,会报错
new实例化的构造函数指向创建的对象实例
定时器中的this还指向window
事件、对象中的this还指向调用者
3> 函数变化
函数不能有重名的参数
函数必须声明在顶层,且不允许在非函数的代码块内声明函数

'use strict';

if (true) {
	function f () {} // 语法错误
	f()
}

for (var i = 0; i < 5; i++) {
	function f2 () {}  // 语法错误
	f2()
}

function f3 () { // 合法
	function f4 () {} // 合法
}

更多严格模式要求

  • 高阶函数
    高阶函数是对其他函数进行操作的函数,它接收函数作为参数将函数作为返回值输出
// 作为参数
function fn (callback) {
	callback && callback() // 如果存在callback 则执行
}
fn(function () {alert('hi')})

// 作为返回值
function fn () {
	return function () {}
}
fn()

此时fn就是高阶函数,函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用。最典型的是作为回调函数

  • 闭包
    闭包(closure)指有权访问另一个函数作用域中变量的函数。即,一个作用域访问另外一个函数内部的局部变量,就产生了闭包(被访问的变量所在的函数就是闭包函数)
function fn () {
	var num = 10
	function fun () {
		console.log(num)
	}
	fun()
}
fn()
// 在fn外的作用域可以访问fn内部的局部变量
function fn () {
	var num = 10
	// function fun () {
	// 	console.log(num)
	// }
	// return fun()
	// 另一种写法
	return function () {
		console.log(num)
	}
}
var f = fn()
f()
//类似于
// var f = function fun () {
//		console.log(num)
//	}

闭包的主要作用:延伸了变量的作用范围

  • 递归
4. 递归

如果一个函数在内部可以调用其本身,这个函数就是递归函数(函数内部自己调用自己)
递归的作用和循环效果一样
递归很容易发生“栈溢出”错误,所以必须加退出条件return

5. 浅拷贝和深拷贝

浅拷贝只是拷贝一层,更深层次对象级别的只拷贝了其引用(地址),此时数据指向同一个地址,修改任意一项的数据,另一项也都会改变

  • es6新增浅拷贝方法
    Object.assign(target, ...sources)
    Object.assign(o, obj) 即,把obj拷贝给o

浅拷贝

var obj = {
	id: 1,
	name: 'andy',
	msg: {
		age: 18
	}
}

// 浅拷贝
// 修改o[msg]的值,obj中的值也会改变
var o = {}
for (var k in obj) {
	// k是属性名,obj[k]是属性值
	o[k] = obj[k]
}
o.msg.age = 20
console.log(obj.msg.age) // 20

深拷贝拷贝多层,每一级别的数据都会拷贝

var obj = {
	 id: 1,
     name: '罗飞',
     msg: {
         age: 18
     },
     friend: [
         '王子麒',
         '王剑'
     ]
 }

 var o = {}
 // instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
 function deepCopy (newObj, oldObj) {
     for (var k in oldObj) {
         // 判断属性值属于哪种数据类型
         // 1. 获取属性值
         var item = oldObj[k]
         // 2. 判断是否为数组
         // 数组也属于object,如果先判断是否为object,会把数组当对象解析,就不会再判断array了
         if (item instanceof Array) {
             newObj[k] = []
             deepCopy(newObj[k], item)
         } else if (item instanceof Object) {
             // 3. 判断是否为对象
             newObj[k] = {}
             deepCopy(newObj[k], item)
         } else {
             // 4. 判断是否为普通数据类型
             newObj[k] = item
         }
     }
 }
 deepCopy(o, obj)
 console.log(o);
 o.msg.age = 24
 console.log(obj);

深拷贝是把内存空间中的数据复制了一份给新拷贝的项,所以修改任意都不会有影响
深拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值