【JavaScript】作用域、深浅拷贝、原型

JS

1.1作用域和闭包、this

(1)作用域和自由变量

作用域就是变量的一个合法使用范围

变量可以在自己对应的红框的任何位置使用

(2)作用域分为以下几类:

1.全局作用域

2.函数作用域

3.块级作用域(ES6新增)

if (true) {
   let x = 100
}
console.log(x) // 报错

(3)自由变量

  • 一个变量在当前作用域没有定义,但被使用了
  • 向上级作用域,一层一层一次寻找,直到找到为止
  • 如果到全局作用域都没找到,则报错xx is not definded

(4) 闭包

  • 作用域应用的特殊情况,有两种表现:

    • 函数作为参数被传递
    • 函数作为返回值被返回

    所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方。

    // 闭包
    // 函数作为返回值
    function create() {
        let a = 100
        return function () {
            console.log(a)
        }
    }
    const fn = create()
    const a = 200
    fn() //a = 100
    
    //函数作为参数
    function print(fn){
        const a = 200
        fn()
    }
    const a = 100
    function fn(){
        console.log(a)
    }
    print(fn) //100
    

(5)闭包的应用

  1. 隐藏数据
  2. 如做一个简单的cache工具
// 闭包隐藏数据 ,只提供 API
function createCache() {
    const data  = {} // 闭包中的数据,被隐藏,不被外界访问
    return {
        set: function (key, val) {
            data[key] = val
        },
        get: function (key) {
            return data[key]
        }
    }
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
// 用户能用到set get 的api,但是不能够直接操作data数据

重点区别:

// 创建10 个a标签,点击的时候弹出对应的序号
let a
for (let i = 0; i < 10; i++) {
    a = document.createElement('a')
    // 这里不能用innerText给a赋标签内容,需要使用innerHTML,浏览器才能识别
    a.innerHTML = i+'<br>'
    a.addEventListener('click',function(e){
        e.preventDefault()
        alert(i)
        
    })
    document.body.appendChild(a)
}
// !!!上面这段代码,点击连接弹出0-9,并不是10.因为let i 是 for 作用域中的内容,每次点击就会在作用域查找上一次的值
let i, a
for (i = 0; i < 10; i++) {
    a = document.createElement('a')
    // 这里不能用innerText给a赋标签内容,需要使用innerHTML,浏览器才能识别
    a.innerHTML = i+'<br>'
    a.addEventListener('click',function(e){
        e.preventDefault()
        alert(i)
        
    })
    document.body.appendChild(a)
}
// !!!上面这段代码,点击链接时只会弹出10,因为此时全局变量,再点击之前i就已经被累加到了10

(6)this

  1. 作为普通函数

  2. 使用callapplybind

  3. 作为对象方法被调用

  4. class方法中调用

  5. 箭头函数

this取什么样的值,是在执行的时候确认,不是在函数定义的时候确认的。

// this
function fn1() {
    console.log(this)
}
fn1()  //window

fn1.call({x: 100}) // {x:100}
const fn2 = fn1.bind({x: 200}) // {x:200}
fn2()

const zhangsan1 = {
    name: '张三',
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    wait() {
        setTimeout(function() {
            // this === window
            console.log(this)
        })
    }
}


const zhangsan2  = {
    name: '张三',
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    waitAgain() {
        setTimeout(() => {
            // this 即当前对象,箭头函数的this取值是取他上级作用域的取值
            console.log(this)
        })
    }
}

class People {
    constructor(name) {
        this.name = name
        this.age = 20
    }
    sayHi() {
        console.log(this)
    }
}
const zhangsan = new People('张三')
zhangsan.sayHi() // zhangsan 对象

(7)手写bind函数

Function.prototype.bind1 = function () {
 	// 将参数解析为数组
   const args = Array.prototype.slice.call(arguments)
   // 获取 this (取出数组第一项,数组剩余的就是传递的参数)
   const t = args.shift()
   const self = this //当前函数
   //返回一个函数
   return function () {
       // 执行原函数,并返回结果
       return self.apply(t,args)
   }
}

1.2 值类型和引用类型的区别

常见的值类型 :

undefined 、 字符串 、 数值 、布尔 、Symbol(‘s’)(es6)

常见的引用类型:

对象 、 数组 、null(特殊引用类型,指针指向为空地址)、函数(特殊引用类型,它不用于存储数据,所以没有“拷贝、复制函数”一说)

用const定义常量必须赋值

1.3 typeof 运算符

  • 识别所有值类型
  • 识别函数
  • 判断是否是引用类型(不可再细分)
// 判断所有值类型
let a;
const str = "abc";
const n = 100;
const b = true;
const s = Symbol('s');

typeof a // 'undefined'
typeof str // 'string'
typeof n // 'number'
typeof b // 'boolean'
typeof s // 'symbol'

// 能判断函数
typeof console.log // 'function'
typeof funtion () {} // 'function'

// 能识别引用类型(不能具体属于应用类型中的哪一类)
typeof null // 'object'
typeof ['a','b'] // 'object'
typeof { x: 100} // 'object'

1.4 深拷贝和浅拷贝

浅拷贝

在js默认情况下,对象赋值是一个浅拷贝。

obj1 = {
	name: "国福",
	age: 56,
	address: {
		city: 'beijing'
	},
	arr: ['a','b','c']
}
const obj2 = obj1
obj2.address.city = 'shanghai'
console.log(obj1.address.city) // shanghai

深拷贝(手写深拷贝)

  • 注意判断值类型和引用类型
  • 注意判断是数组还是对象
  • 递归
// Obj1实例
let obj1 = {
    name: "双越",
    age: 15,
    address:{
        city:"北京"
    },
    hobby:['写书','写诗','跑步']
}
console.log(obj1)//15
let obj2 = deepClone(obj1)
obj2.age=17
console.log(obj1)//15
let obj3 = obj1
obj3.age = 34
console.log(obj1)//34


/**
 * 深拷贝
 * @param {Object} obj 
 */
function deepClone (obj) {
    if (typeof(obj) !== 'object'|| obj == null) {
        // obj 是 null,或者不是对象和数组,直接返回
        return obj
    }
    // 初始化返回结果
    let result
    // instanceof 判断obj具体为什么引用类型
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // hasOwnProperty 判断属性是否属于对象
        if (obj.hasOwnProperty(key)) {
            // 保证 key 不是原型的属性,而是自身属性
            result[key] = deepClone(obj[key])
        }
    }
    return result
}

1.5 变量计算-类型转换

  • 字符串拼接

  • == 运算符

    100 == '100' //true
    0 == '' //true
    0 == false //true
    flase == '' //true
    null == undefinded //true
    
    // 除了 == mull 之外,其他都一律用 ===,例如
    const obj = { x: 100}
    if (obj.a == null) {
    }
    // 相当于,if (obj.a === null || obj.a === undefinded) {}
    
  • 逻辑判断

    • console.log(10 && 0) //0
      console.log('' || 'abc') // 'abc'
      console.log(!window.abc) // true
      

1.6 类型判断 -instanceof

instanceof 的实现原理为:判断一个实例属于不属于一个类型,当根据实例的隐式原型逐层寻找时,隐式原型形成的原型链所涉及到的类,实例都是属于的。


[] instanceof Array //true
[] instanceof Object //true

{} instanceof Object //true

1.7 原型

__proto__:隐式原型

prototype:显示原型

当一个类继承另一个类时,父类的显式原型与子类的隐式原型是相同的。

在这里插入图片描述

  • 每个class都有显示原型 prototype
  • 每个实例都有隐式原型 __proto__
  • 实例的__proto__指向对应classprototype
class Person{
    constructor(namee,age,language){
        this.namee = namee
        this.age = age
        this.language = language
    }
    speak() {
        console.log(`我${this.namee}能说${this.language}`)
    }
}

class Student extends Person {
    constructor(namee,age,language,subject){
        // 父类的构造函数参数
        super(namee,age,language)
        this.subject = subject
    }
    listen() {
        console.log(`${this.namee}喜欢听${this.subject}`)
    }
}

let xialuo = new Student('夏洛',25,'中文','语文课')
xialuo.listen()

console.log(xialuo instanceof Person)//true
console.log(Student.prototype.__proto__ === Person.prototype) //true
console.log(xialuo.hasOwnProperty('speak')) // 键名需要用单引号引 false,hasOwnProperty 判断的属性是否属于当前对戏

基于原型的执行规则

  1. 获取属性或执行方法时。
  2. 现在自身属性和方法寻找
  3. 如果找不到自动去__proto__中查找

注:

classES6语法规范,由ECMA委员会发布

ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现

1.8 手动封装一个 JQuery

本质上就是一个jQuery对象+js中的DOM-API。

class jQuery {
    constructor(selector) {
        const result = document.querySelectorAll(selector)
        const length = result.length
        for (let i = 0; i < length; i++) {
            this[i] = result[i]
        }
        this.length = length
        this.selector = selector
        //类似数组的形式,实际是对象,jQuery的对象
    }
    get(index) {
        return this[index]
    }
    each(fn) {
        for (let i = 0; i < this.length; i++) {
            const elem = this[i]
            fn(elem)
        }
    }
    on(type, fn) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}


// 插件,扩展性的一种方式,向jQuery对象的显示原型中直接添加属性或方法
jQuery.prototype.dialog = function (info) {
    alert(info)
}
// 复写机制(造轮子)
// 基于jQuery写
class myjQuery extends jQuery {
    constructor(selector) {
        super(selector)
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值