一次搞懂前端this、闭包、作用域,就用代码来理解

简介: 前端this、闭包、作用域

闭包的应用

闭包是指有权访问另外一个函数作用域中的变量的函数.可以理解为(能够读取其他函数内部变量的函数)

0. 封装私有变量

function Person0() {
    this._attackVolume = 100;
}
Person0.prototype = {
    /** ... **/
};
var p0 = new Person0(); console.log(p0._attackVolume); // 100

// 工厂方法
function Person1() {
    var _attackVolume = 100;
    return {
        attack() {
            console.log('attack ' + _attackVolume);
            _attackVolume++
        }
    };
}
var p1 = new Person1(); console.log(p1._attackVolume); // undefined
p1.attack() // attack 101
p1.attack() // attack 100

1. 储存变量(缓存)

function getListDataManager() {
    let localData = null;
    return {
        getData() {
            if (localData) {
                return Promise.resolve(localData); // 返回的是储存的结果
            }
            return fetch('xxxx').then(data => localData = data.json()); // 真实的数据请求
        }
    };
}

2. 防抖节流函数的实现

参考上一篇文章:解析几个JS手写函数(call、防抖节流)

this的5种场景

0. 函数直接调用

function myfunc() {
    console.log(this)
}
myfunc(); // this是window, 严格模式下是undefined

逗号表达式情况:

var obj = {
    show: function () {
        console.log('this:', this);
    }
};
(0, obj.show)(); // window
// 等于 var re = function () { console.lo... } ; re();

1. 函数被别人调用时

function myfunc() {
    console.log(this)
}
var a = {
    myfunc: myfunc
};
a.myfunc(); // this是对象a

看似调用的形式,实则直接调用的情况:

function run() {
    console.log(this);
}
var obj = {
    test: function () {
        run()
    }
}
obj.test() // 这里this是window

谁调用this就是谁:

var obj = {
    sub: {
        show: function () {
            console.log('this:', this);
        }
    }
};
obj.sub.show() // this为对象sub

2. new创建实例时

function Person(name) {
    this.name = name;
    console.log(this);
}

var p = new Person('name'); // p

var obj = {
    show: function () {
        console.log('this:', this);
    }
};
var newobj = new obj.show(); // newobj

new > bind > 调用

var obj = {
    show: function () {
        console.log('\nthis:\n\n', this);
    }
};
obj.show(); // obj
(obj.show.bind(obj))(); // obj
var newobj = new (obj.show.bind(obj))(); // newobj

3. apply、call、bind时

一般bind要优与call/apply。(call参数多,apply参数少)

function getColor(color) {
    this.color = color;
    console.log(this);
}
function Car(name, color) {
    this.name = name; // this指的是实例car
    console.log(this)
    getColor.call(this, color); // 这里的this从原本的getColor,变成了car
}
var car = new Car('卡⻋', '绿色');

4. 箭头函数

没有自己的this,也不能被new关键字调用

var a = {
    myfunc: function () {
        setTimeout(() => {
            console.log(this);
        }, 0)
    },
    myfunc2: function () {
        setTimeout(function() {
            console.log(this);
        }, 0)
    }
};
a.myfunc(); // this是a
a.myfunc2(); // this是setTimeout

5. 综合应用

var obj = {
    show: function () {
        console.log('this:', this);
    }
};
var elem = document.getElementById('xxx');
elem.addEventListener('click', obj.show); // 传递给了click事件,执行时相当于直接调用,this指window
elem.addEventListener('click', obj.show.bind(obj)); // 显式绑定了obj,this指obj
elem.addEventListener('click', function () {
    obj.show(); // 执行是调用出来的,this指obj
});

作用域

for (var i = 0; i < 10; i++) {
    console.log(i); // 0 1 2 .... 9
}
for (var i = 0; i < 10; i++) {
    setTimeout(function () { // 时间循环,进入异步队列,调用时主栈for已执行完,i的值为10
        console.log(i); // 10
    }, 0);
}
for (var i = 0; i < 10; i++) {
    (function (i) { // 独立的10个作用域块了
        setTimeout(function () {
            console.log(i); // 0 1 2 3 .... 9
        }, 0)
    })(i);
}
for (let i = 0; i < 10; i++) {
    console.log(i); // 0 1 2 3 .... 9
}

被JS变量提升影响:

var person = 1;
function showPerson() {
    console.log(person); // 如果没有var定义的person那么输出是1
    var person = 2; // 变量提升:函数showPerson作用域内person被提前声明初始化并赋值undefind
}
showPerson(); // undefined

被JS函数提升影响:

var person = 1;
function showPerson() {
    console.log(person);
    var person = 2; // 在函数作用域内任何地方定义此person均不会影响结果
    function person() { } // 函数提升:提前声明初始化并赋值整个函数内容
}
showPerson(); // function person() { }

面向对象

function Person() {
    this.name = 1;
}
Person.prototype = {
    name: 2,
    show: function () {
        console.log('name is:', this.name);
    }
};

var person = new Person();
person.show(); // name is: 1
Person.prototype.show(); // name is: 2

Person.prototype.show = function () {
    console.log('new show');
};
person.show(); // new show
相关文章
|
9天前
|
存储 前端开发 JavaScript
前端必备知识:闭包的概念、作用与应用
前端必备知识:闭包的概念、作用与应用
12 1
|
14天前
|
前端开发 JavaScript 开发者
利用代码分割优化前端性能:高级技巧与实践
【10月更文挑战第2天】在现代Web开发中,代码分割是优化前端性能的关键技术,可显著减少页面加载时间。本文详细探讨了代码分割的基本原理及其实现方法,包括自动与手动分割、预加载与预取、动态导入及按需加载CSS等高级技巧,旨在帮助开发者提升Web应用性能,改善用户体验。
|
24天前
|
前端开发 小程序 JavaScript
信前端里的循环显示如何编写代码?
信前端里的循环显示如何编写代码?
60 5
|
2月前
|
JavaScript 前端开发 小程序
【技巧】JS代码这么写,前端小姐姐都会爱上你
本文介绍了JavaScript编程中的实用技巧,包括解构赋值的多种妙用、数组操作技巧及常用JS功能片段。解构赋值部分涵盖短路语法防错、深度解构及默认值赋值;数组技巧包括按条件添加数据、获取最后一个元素及使用`includes`优化`if`语句;常用功能片段则涉及URL参数解析、页面滚动回顶部及获取滚动距离等。通过这些技巧,提升代码质量和效率。
25 0
【技巧】JS代码这么写,前端小姐姐都会爱上你
|
2月前
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
74 0
|
2月前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
119 0
|
2月前
|
前端开发 开发者 Apache
揭秘Apache Wicket项目结构:如何打造Web应用的钢铁长城,告别混乱代码!
【8月更文挑战第31天】Apache Wicket凭借其组件化设计深受Java Web开发者青睐。本文详细解析了Wicket项目结构,帮助你构建可维护的大型Web应用。通过示例展示了如何使用Maven管理依赖,并组织页面、组件及业务逻辑,确保代码清晰易懂。Wicket提供的页面继承、组件重用等功能进一步增强了项目的可维护性和扩展性。掌握这些技巧,能够显著提升开发效率,构建更稳定的Web应用。
89 0
|
2月前
|
前端开发 程序员 API
从后端到前端的无缝切换:一名C#程序员如何借助Blazor技术实现全栈开发的梦想——深入解析Blazor框架下的Web应用构建之旅,附带实战代码示例与项目配置技巧揭露
【8月更文挑战第31天】本文通过详细步骤和代码示例,介绍了如何利用 Blazor 构建全栈 Web 应用。从创建新的 Blazor WebAssembly 项目开始,逐步演示了前后端分离的服务架构设计,包括 REST API 的设置及 Blazor 组件的数据展示。通过整合前后端逻辑,C# 开发者能够在统一环境中实现高效且一致的全栈开发。Blazor 的引入不仅简化了 Web 应用开发流程,还为习惯于后端开发的程序员提供了进入前端世界的桥梁。
173 0
|
2月前
|
JavaScript 前端开发
揭秘Vue.js组件魔法:如何轻松驾驭前端代码,让维护变得轻而易举?
【8月更文挑战第30天】本文探讨了如何利用Vue.js的组件化开发提升前端代码的可维护性。组件化开发将复杂页面拆分为独立、可复用的组件,提高开发效率和代码可维护性。Vue.js支持全局及局部组件注册,并提供了多种组件间通信方式如props、事件等。通过示例展示了组件定义、数据传递及复用组合的方法,强调了组件化开发在实际项目中的重要性。
33 0
|
5天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。