网易前端微专业课程讨论区问答精选

本文整理自网易前端微专业课程讨论区,涵盖JavaScript程序设计、DOM编程艺术等多个部分,深入探讨了arguments.callee、instanceof、闭包、函数值传递、原型与构造函数的区别、调试技巧、内存管理、DOM操作等核心概念,同时涉及到了浏览器兼容性、单页应用原理、模块化、数据存储选择等问题,旨在帮助读者巩固和提升前端开发技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Javascript程序设计部分

注:以下内容大部分来自网易云课堂《前端微专业》课程讨论区,下问内容较多,可打开标题下方的[目录]索引,便于定位查看。

arguments.callee()方法?

[严老师]功能:arguments.callee是当前被调用函数自身引用,主要用于匿名函数调用时访问函数本身,比如用匿名函数计算某个整数阶乘这样的功能。
产生原因及现状:早期的js版本不允许命名函数表达式,因此不能构造一个递归的函数表达式,为此引入了arguments.callee这样一个属性,但实际上这是一个糟糕解决方案,ES3通过命名函数表达式方式解决了函数递归的问题。callee在ES5严格模式中是被禁用的。
[daniel补充]“命名函数表达式”参考链接

instanceof 中提到的“不能判别原始类型” 这个“原始类型”是指 前面的6个“标准类型”吗?

当前ECMAScript 中数据类型有6种:String, Number, Boolean, Null, Undefined, Object. Object是引用类型,前5种统称为原始(primitive)类型。JavaScript data types and data structures
instanceof总结:
1. 判别内置对象类型
2. 不能判别原始类型
3. 判别自定义对象类型
这里写图片描述
[陈同学归纳]
这里写图片描述

类型判断方法问题

function type(obj){
   
   
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); 
}

[严老师]
1. call是Function的原型对象方法(类似方法还有apply),用于指定Function实例对象(即函数)的调用者和参数并实现函数调用。apply和call会在“内置对象-Function”课程中为同学详细讲解,请关注。
2. slice和toLowerCase都是String的原型对象方法,即所有String类型对象(或字符串字面量)都能直接调用到的方法。具体功能参见ls同学回答。当然String的原型对象方法远不止这两个,同学可以去W3C或MDN网站了解各个原型方法功能和语法。
[daniel补充]MDN对arguments.callee的描述

单个JS文件如何调试

[严老师]有两种方式,一种是把js写在html文件内,然后用浏览器调试该文件,在相应的语句处设置断点,当断点被断住时,就能查看到当前运行环境各种变量值。
另一种方法是直接在chrome console中执行,用”debugger”执行调试,同样也能在“Scope Variables”查看到当前运行环境各种变量值;也方便观测方式是可以在“Watch Expressions”中添加需观测变量。如下图

函数值传递问题

以下代码执行后,president.name=?

function setName(obj){
   
   
    obj.name = "obama";
    obj = {name:"clinton"};
}
var president = {name:"bush"};
setName(president);

[daniel]输出obama
进入setname后,obj 和 president指向堆内存中同一对象,对obj的修改会反应到president上,而第二行执行后,obj指向一个新的对象clinton,此时president依然指向obama
[严老师]js中=实现就是值类型复制,当对方是引用类型时复制的就是该引用类型的地址值。你例子中,当setname方法被调用时,参数obj接收到的值是对象{name:”soul”}的地址,当然你可以更改该对象内的name属性。但是当执行obj={name:”new name”}时,obj被修改为{name:”new name”}地址值,显然这两个地址实际指向不同对象,因此外面nameobj不会受到影响。不知这样解释是否明白了
[yinchenhao0]函数调用策略的补充
从语言的层面上说, 这种函数调用策略叫call by sharing
与之相对的叫 call by value 和 call by reference

proto 与 prototype 的区别 ?老师参与

function Point(){}
var p = new Point();

// 能解释一下:
Point.__proto__         // Function Empty()
Point.prototype         // Point {}
p.__proto__                // Point {}
p.prototype                // undefined

[严老师]prototype是构造器对象属性,构造器实例化对象时,prototype会被实例对象的proto所引用。比如

var s = new String("abc");
s.__proto__ === String.prototype;  // true

[yinchenhao0]
这里写图片描述
注:
这节论坛回复中,严老师贴了很多站内链接,现在全部失效了。。。

关于prototype和constructor的疑问

  • 什么样的对象具有prototype属性?是否所有对象都有这个属性(包括对象直接量)?
  • 什么样的对象具有constructor属性?是否只有用new加构造器构造出来的对象有?
  • prototype和constructor有什么样的联系呢?还是独立的概念没有联系

[严老师]
1. 构造器对象有prototype(即原型对象)属性,普通对象没有。(普通对象只有引用了prototype的proto隐藏属性)
2. prototype(即原型对象)有constructor属性。普通对象可以隐式的通过原型链(proto)访问到constructor。
3. constructor是prototype的一个属性。

栈内存,堆内存指什么?

[严老师]
因为js中开发则不能直接操作内存,因此js栈内存可以简单的理解为原始类型值和引用类型地址存储区域(如果是临时变量,用完后会被回收),堆内存表示引用类型值存储区域。
在数据结构知识体系里,栈表示先进后出(First-In/Last-Out)的一种数据结构,堆表示队列优先级的一种数据结构。
1、在C/C++内存存储知识体系里:栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

console调试器如何使用?

[不要叫我同学]
很有用,推荐看下。链接

请问在内置对象->Function中, 把参数赋值为null是为了释放内存么

var define = function(name, deps, callback){
   
   
    var node, context;
    if(typeof(name) !== "string"){
        // foo code

        // 下面这三行没太看懂, 是为了释放内存么?
        callback = deps;
        deps = name;
        name = null;
    }
};

[严老师]
define是require.js中定义模块的函数。define函数允许调用者传1个参数,2个参数和3个参数的情况
传1个参数时,默认这个参数须是callback函数
传2个参数时,默认一个是deps数组,另一个是callback函数
传3个参数时,分别对应name名称,deps数组和callback函数
比如我下面这样调用时

define([],function{
   
   });

刚进入函数执行时,name=[],deps=function(){},callback=undefined,此时如果不做更改,函数后面的逻辑就会出错。

if(typeof(name) !== "string"){
    callback = deps;
    deps = name;
    name = null;
}

上面的代码就是通过对参数类型识别将实参的值赋值给正确的得形参,以保证函数后面逻辑正确的执行。其实就是基于参数个数和类别的函数功能重载。
另外name是define函数定义的临时变量,显式将它置为null,通常可能有两个原因:
1. 后面处理name时,便于判断,而不需要再次通过类型识别去判别。
2. 有可能name会在后面代码中会被其他全局变量引用到,这样的话确实可能会影响到浏览器内存回收。
建议lz去require.js中读下这个代码,可能比我直接讲理解更深入,还能了解下require.js啊。

闭包的概念及用法

[严老师]
闭包的作用是对对象私有属性或方法进行封装(参考C++和JAVA中的公有、私有属性区别和目的),有些只跟对象自身相关的属性或方法不希望让对象使用者直接访问到时我们就需要使用闭包。
比如有台空调,它有个内部状态属性(两个值:0表示不运行,1表示运行中),但是使用者不能直接更改这个属性,因为0(不运行)-〉1(运行中)状态切换时,可能还有很多其他工作要做(比如要读取上次空调的设置(制冷还是制热)等一系列动作),直接更改状态位很可能导致空调内部状态混乱甚至运行错误。因此使用者只能调用空调对象开放出来的开关方法switchOn()、switchOff()来进行空调的开关控制(这些方法往往代表一个完整的控制逻辑)。
这个例子中,空调的状态位就应该用闭包将其封装为私有属性。如果你设计开发的对象、库、框架设计会被其他人使用,就很有必要对此进行合理的封装。
上面提到的是闭包的通常使用场景。闭包准确的定义是函数执行作用域上的一个扩展作用域,

function func(){
   
   
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值