JavaScript全面指南(五)

news2024/11/29 22:43:45

​🌈个人主页:前端青山
🔥系列专栏:JavaScript篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript全面指南

目录

81、ES6 class关键字原理跟function什么区别

82、如何检查一个数字是否为整数?

83、 什么是 IIFE(立即调用函数表达式)?

84、如何在 JavaScript 中比较两个对象?

85、 你能解释一下 ES5 和 ES6 之间的区别吗?

86、 解释 JavaScript 中“undefined”和“not defined”之间的区别。

87、如何在 JavaScript 中创建私有变量?

88、 请解释原型设计模式。

89、模板引擎原理

90、 map和foreach的区别

91、es6的新特性

92、如何向 Array 对象添加自定义方法,让下面的代码可以运行?

93、0.1+0.2等不等于0.3?自己封装一个让他们相等的方法

94、跨域是什么?有哪些解决跨域的方法和方案?

95、什么是函数式编程?什么的声明式编程?

96、super() 是否必须执行?不执行怎么让它不报错?

97、什么是 JavaScript 中的提升操作?

98、 以下代码输出的结果是什么?

99、图片懒加载怎么实现

100、for-in 循环会遍历出原型上的属性吗?


81、ES6 class关键字原理跟function什么区别

传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。这样的写法相对于其它传统面向对象语言来讲,很有一种独树一帜的感脚!非常容易让人困惑! 如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。构造函数示例:


//函数名和实例化构造名相同且大写(非强制,但这么写有助于区分构造函数和普通函数)
function Person(name,age) {
    this.name = name;
    this.age=age;
}
Person.prototype.say = function(){
    return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
var obj=new Person("laotie",88);//通过构造函数创建对象,必须使用new 运算符
console.log(obj.say());//我的名字叫laotie今年88岁了

构造函数生成实例的执行过程:

1.当使用了构造函数,并且new 构造函数(),后台会隐式执行new Object()创建对象;
2.将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象。
3.执行构造函数的代码。
4.返回新对象(后台直接返回);

ES6引入了Class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言。如果将之前的代码改为ES6的写法就会是这个样子:


class Person{//定义了一个名字为Person的类
    constructor(name,age){//constructor是一个构造方法,用来接收参数
        this.name = name;//this代表的是实例对象
        this.age=age;
    }
    say(){//这是一个类的方法,注意千万不要加上function
        return "我的名字叫" + this.name+"今年"+this.age+"岁了";
    }
}
var obj=new Person("laotie",88);
console.log(obj.say());//我的名字叫laotie今年88岁了

注意项

1.在类中声明方法的时候,千万不要给该方法加上function关键字
2.方法之间不要用逗号分隔,否则会报错

由下面代码可以看出类实质上就是一个函数。类自身指向的就是构造函数。所以可以认为ES6中的类其实就是构造函数的另外一种写法!


console.log(typeof Person);//function
console.log(Person===Person.prototype.constructor);//true

以下代码说明构造函数的prototype属性,在ES6的类中依然存在着。 console.log(Person.prototype);//输出的结果是一个对象 实际上类的所有方法都定义在类的prototype属性上。代码证明下:


Person.prototype.say=function(){//定义与类中相同名字的方法。成功实现了覆盖!
    return "我是来证明的,你叫" + this.name+"今年"+this.age+"岁了";
}
var obj=new Person("laotie",88);
console.log(obj.say());//我是来证明的,你叫laotie今年88岁了

当然也可以通过prototype属性对类添加方法。如下:


Person.prototype.addFn=function(){
    return "我是通过prototype新增加的方法,名字叫addFn";
}
var obj=new Person("laotie",88);
console.log(obj.addFn());//我是通过prototype新增加的方法,名字叫addFn

还可以通过Object.assign方法来为对象动态增加方法

Object.assign(Person.prototype,{
    getName:function(){
        return this.name;
    },
    getAge:function(){
        return this.age;
    }
})
var obj=new Person("laotie",88);
console.log(obj.getName());//laotie
console.log(obj.getAge());//88

constructor方法是类的构造函数的默认方法,通过new命令生成对象实例时,自动调用该方法。


class Box{
    constructor(){
        console.log("啦啦啦,今天天气好晴朗");//当实例化对象时该行代码会执行。
    }
}
var obj=new Box();

constructor方法如果没有显式定义,会隐式生成一个constructor方法。所以即使你没有添加构造函数,构造函数也是存在的。constructor方法默认返回实例对象this,但是也可以指定constructor方法返回一个全新的对象,让返回的实例对象不是该类的实例。


class Desk{
    constructor(){
        this.xixi="我是一只小小小小鸟!哦";
    }
}
class Box{
    constructor(){
       return new Desk();// 这里没有用this哦,直接返回一个全新的对象
    }
}
var obj=new Box();
console.log(obj.xixi);//我是一只小小小小鸟!哦

constructor中定义的属性可以称为实例属性(即定义在this对象上),constructor外声明的属性都是定义在原型上的,可以称为原型属性(即定义在class上)。hasOwnProperty()函数用于判断属性是否是实例属性。其结果是一个布尔值, true说明是实例属性,false说明不是实例属性。in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。


class Box{
    constructor(num1,num2){
        this.num1 = num1;
        this.num2=num2;
    }
    sum(){
        return num1+num2;
    }
}
var box=new Box(12,88);
console.log(box.hasOwnProperty("num1"));//true
console.log(box.hasOwnProperty("num2"));//true
console.log(box.hasOwnProperty("sum"));//false
console.log("num1" in box);//true
console.log("num2" in box);//true
console.log("sum" in box);//true
console.log("say" in box);//false

类的所有实例共享一个原型对象,它们的原型都是Person.prototype,所以proto属性是相等的


class Box{
    constructor(num1,num2){
        this.num1 = num1;
        this.num2=num2;
    }
    sum(){
        return num1+num2;
    }
}
//box1与box2都是Box的实例。它们的__proto__都指向Box的prototype
var box1=new Box(12,88);
var box2=new Box(40,60);
console.log(box1.__proto__===box2.__proto__);//true

由此,也可以通过proto来为类增加方法。使用实例的proto属性改写原型,会改变Class的原始定义,影响到所有实例,所以不推荐使用!


class Box{
    constructor(num1,num2){
        this.num1 = num1;
        this.num2=num2;
    }
    sum(){
        return num1+num2;
    }
}
var box1=new Box(12,88);
var box2=new Box(40,60);
box1.__proto__.sub=function(){
    return this.num2-this.num1;
}
console.log(box1.sub());//76
console.log(box2.sub());//20

class不存在变量提升,所以需要先定义再使用。因为ES6不会把类的声明提升到代码头部,但是ES5就不一样,ES5存在变量提升,可以先使用,然后再定义。


//ES5可以先使用再定义,存在变量提升
new A();
function A(){
​
}
//ES6不能先使用再定义,不存在变量提升 会报错
new B();//B is not defined
class B{
​
}

82、如何检查一个数字是否为整数?

检查一个数字是小数还是整数,可以使用一种非常简单的方法,就是将它对 1 进行取模,看看是否有余数。

function isInt(num) {
 return num % 1 === 0;
}
console.log(isInt(4)); // true
console.log(isInt(12.2)); // false
console.log(isInt(0.3)); // false

83、 什么是 IIFE(立即调用函数表达式)?

它是立即调用函数表达式(Immediately-Invoked Function Expression),简称 IIFE。函数被创建后立即被执行:


(function IIFE(){
 console.log( "Hello!" );
})();
// "Hello!"

在避免污染全局命名空间时经常使用这种模式,因为 IIFE(与任何其他正常函数一样)内部的所有变量在其作用域之外都是不可见的。

84、如何在 JavaScript 中比较两个对象?

对于两个非原始值,比如两个对象(包括函数和数组),== 和 === 比较都只是检查它们的引用是否匹配,并不会检查实际引用的内容。

例如,默认情况下,数组将被强制转型成字符串,并使用逗号将数组的所有元素连接起来。所以,两个具有相同内容的数组进行 == 比较时不会相等:

var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false

对于对象的深度比较,可以使用 deep-equal 这个库,或者自己实现递归比较算法

85、 你能解释一下 ES5 和 ES6 之间的区别吗?

  • ECMAScript 5(ES5):ECMAScript 的第 5 版,于 2009 年标准化。这个标准已在所有现代浏览器中完全实现。

  • ECMAScript 6(ES6)或 ECMAScript 2015(ES2015):第 6 版 ECMAScript,于 2015 年标准化。这个标准已在大多数现代浏览器中部分实现。

以下是 ES5 和 ES6 之间的一些主要区别:

箭头函数和字符串插值:


const greetings = (name) => {
 return `hello ${name}`;
}
const greetings = name => `hello ${name}`;

常量

常量在很多方面与其他语言中的常量一样,但有一些需要注意的地方。常量表示对值的“固定引用”。因此,在使用常量时,你实际上可以改变变量所引用的对象的属性,但无法改变引用本身。

const NAMES = [];
NAMES.push("Jim");
console.log(NAMES.length === 1); // true
NAMES = ["Steve", "John"]; // error

块作用域变量。

新的 ES6 关键字 let 允许开发人员声明块级别作用域的变量。let 不像 var 那样可以进行提升。

默认参数值

默认参数允许我们使用默认值初始化函数。如果省略或未定义参数,则使用默认值,也就是说 null 是有效值。

// 基本语法
function multiply (a, b = 2) {
 return a * b;
}
multiply(5); // 10

类定义和继承

ES6 引入了对类(关键字 class)、构造函数(关键字 constructor)和用于继承的 extend 关键字的支持。

for…of 操作符

for…of 语句将创建一个遍历可迭代对象的循环。

用于对象合并的 Spread 操作


const obj1 = { a: 1, b: 2 }
const obj2 = { a: 2, c: 3, d: 4}
const obj3 = {...obj1, ...obj2}

promise

promise 提供了一种机制来处理异步操作结果。你可以使用回调来达到同样的目的,但是 promise 通过方法链接和简洁的错误处理带来了更高的可读性。

const isGreater = (a, b) => {
return new Promise ((resolve, reject) => {
 if(a > b) {
 resolve(true)
 } else {
 reject(false)
 }
 })
}
isGreater(1, 2)
.then(result => {
 console.log('greater')
})
.catch(result => {
 console.log('smaller')
})

模块导出和导入


const myModule = { x: 1, y: () => { console.log('This is ES5') }}
export default myModule;
import myModule from './myModule';

86、 解释 JavaScript 中“undefined”和“not defined”之间的区别。

在 JavaScript 中,如果你试图使用一个不存在且尚未声明的变量,JavaScript 将抛出错误“var name is not defined”,让后脚本将停止运行。但如果你使用 typeof undeclared_variable,它将返回 undefined。

在进一步讨论之前,先让我们理解声明和定义之间的区别。

“var x”表示一个声明,因为你没有定义它的值是什么,你只是声明它的存在。

var x; // 声明 x
console.log(x); // 输出: undefined

“var x = 1”既是声明又是定义(我们也可以说它是初始化),x 变量的声明和赋值相继发生。在 JavaScript 中,每个变量声明和函数声明都被带到了当前作用域的顶部,然后进行赋值,这个过程被称为提升(hoisting)。

当我们试图访问一个被声明但未被定义的变量时,会出现 undefined 错误。

var x; // 声明
if(typeof x === 'undefined') // 将返回 true
当我们试图引用一个既未声明也未定义的变量时,将会出现 not defined 错误。



console.log(y); // 输出: ReferenceError: y is not defined

87、如何在 JavaScript 中创建私有变量?

要在 JavaScript 中创建无法被修改的私有变量,你需要将其创建为函数中的局部变量。即使这个函数被调用,也无法在函数之外访问这个变量。例如:

function func() {
 var priv = "secret code";
}
console.log(priv); // throws error

要访问这个变量,需要创建一个返回私有变量的辅助函数。


function func() {
 var priv = "secret code";
 return function() {
 return priv;
 }
}
var getPriv = func();
console.log(getPriv()); // => secret code

88、 请解释原型设计模式。

原型模式可用于创建新对象,但它创建的不是非初始化的对象,而是使用原型对象(或样本对象)的值进行初始化的对象。原型模式也称为属性模式。

原型模式在初始化业务对象时非常有用,业务对象的值与数据库中的默认值相匹配。原型对象中的默认值被复制到新创建的业务对象中。

经典的编程语言很少使用原型模式,但作为原型语言的 JavaScript 在构造新对象及其原型时使用了这个模式。

89、模板引擎原理

板引擎是通过字符串拼接得到的

let template = 'hello <% name %>!'
let template = 'hello ' + name + '!'
12

字符串是通过new Function执行的

let name = 'world'
let template = `
  let str = 'hello ' +  name  + '!'
  return str
`
let fn = new Function('name', template)
console.log(fn(name)) // hello world!
1234567

将模板转换为字符串并通过函数执行返回

let template = 'hello <% name %>!'
let name = 'world'
function compile (template) {
  let html = template.replace(/<%([\s\S]+?)%>/g, (match, code) => {
    return `' + ${code} + '`
  })
  html = `let str = '${html}'; return str`
  return new Function('name', html)
}
let str = compile(template)
console.log(str(name)) // hello world!
1234567891011

函数只能接收一个name变量作为参数,功能太单一了,一般会通过对象来传参,with来减少变量访问。

with功能

let params = {
  name: '张三',
  age: 18
}
let str = ''
with (params) {
  str = `用户${name}的年龄是${age}岁`
}
console.log(str) // 用户张三的年龄是18岁
123456789

实现简单的模板引擎

let template = 'hello <% name %>!'
let name = 'world'
function compile (template) {
  let html = template.replace(/<%([\s\S]+?)%>/g, (match, code) => {
    return `' + ${code.trim()} + '`
  })
  html = `'${html}'`
  html = `let str = ''; with (params) { str = ${html}; } return str`
  return new Function('params', html)
}
let str = compile(template)
console.log(str({ name })) // hello world!

90、 map和foreach的区别

  • 都是循环遍历数组中的每一项

  • forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项)、index(索引值)、arr(原数组)

  • 匿名函数中的this都是指向window

  • 只能遍历数组

array.map(function(item,index,arr){},this)
Array.forEach(function(item,index,arr){},this)

二、区别:

1.forEach()

没有返回值。

2.map()

有返回值,可以return 出来。

var arr = [1,23,3];
arr.map(function(value,index,array){
  //do something

  return XXX

})

91、es6的新特性

1.不一样的变量声明:const和let

ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部) let和var声明的区别:

var x = '全局变量';
{
  let x = '局部变量';
  console.log(x); // 局部变量
}
console.log(x); // 全局变量

let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了:

const a = 1
a = 0 //报错

如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行:

const student = { name: 'cc' }

student.name = 'yy';// 不报错
student  = { name: 'yy' };// 报错

有几个点需要注意:

  • let 关键词声明的变量不具备变量提升(hoisting)特性

  • let 和 const 声明只在最靠近的一个块中(花括号内)有效

  • 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING

  • const 在声明时必须被赋值

2.模板字符串

在ES6之前,我们往往这么处理模板字符串: 通过“\”和“+”来构建模板

$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");

而对ES6来说

  1. 基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;

  2. ES6反引号(``)直接搞定;

$("body").html(`This demonstrates the output of HTML content to the page, 
including student's ${name}, ${seatNumber}, ${sex} and so on.`);

3.箭头函数(Arrow Functions)

ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;

箭头函数最直观的三个特点。

  • 不需要 function 关键字来创建函数

  • 省略 return 关键字

  • 继承当前上下文的 this 关键字

// ES5
var add = function (a, b) {
    return a + b;
};
// 使用箭头函数
var add = (a, b) => a + b;
​
// ES5
[1,2,3].map((function(x){
    return x + 1;
}).bind(this));
    
// 使用箭头函数
[1,2,3].map(x => x + 1);

细节:当你的函数有且仅有一个参数的时候,是可以省略掉括号的。当你函数返回有且仅有一个表达式的时候可以省略{} 和 return;

  1. 函数的参数默认值

在ES6之前,我们往往这样定义参数的默认值:

// ES6之前,当未传入参数时,text = 'default';
function printText(text) {
    text = text || 'default';
    console.log(text);
}
​
// ES6;
function printText(text = 'default') {
    console.log(text);
}
​
printText('hello'); // hello
printText();// default

5.Spread / Rest 操作符

Spread / Rest 操作符指的是 ...,具体是 Spread 还是 Rest 需要看上下文语境。

当被用于迭代器中时,它是一个 Spread 操作符:

function foo(x,y,z) {
  console.log(x,y,z);
}
 
let arr = [1,2,3];
foo(...arr); // 1 2 3

当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:


function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

6.二进制和八进制字面量

ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:

let oValue = 0o10;
console.log(oValue); // 8
 
let bValue = 0b10; // 二进制使用 `0b` 或者 `0B`
console.log(bValue); // 2

7.对象和数组解构

// 对象
const student = {
    name: 'Sam',
    age: 22,
    sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];
​
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
​
// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);

8.对象超类

ES6 允许在对象中使用 super 方法:


var parent = {
  foo() {
    console.log("Hello from the Parent");
  }
}
 
var child = {
  foo() {
    super.foo();
    console.log("Hello from the Child");
  }
}
 
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
             // Hello from the Child

9.for...of 和 for...in

for...of 用于遍历一个迭代器,如数组:

let letters = ['a', 'b', 'c'];
letters.size = 3;
for (let letter of letters) {
  console.log(letter);
}
// 结果: a, b, c

for...in 用来遍历对象中的属性:

 let stus = ["Sam", "22", "男"];
 for (let stu in stus) {
   console.log(stus[stu]);
  }
// 结果: Sam, 22, 男

10.ES6中的类

ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。

函数中使用 static 关键词定义构造函数的的方法和属性:

class Student {
  constructor() {
    console.log("I'm a student.");
  }
 
  study() {
    console.log('study!');
  }
 
  static read() {
    console.log("Reading Now.");
  }
}
 
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

类中的继承和超集:

class Phone {
  constructor() {
    console.log("I'm a phone.");
  }
}
 
class MI extends Phone {
  constructor() {
    super();
    console.log("I'm a phone designed by xiaomi");
  }
}
 
let mi8 = new MI();

extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。 当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。 在 这里 关于类的介绍。

有几点值得注意的是:

  • 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误

  • 在类中定义函数不需要使用 function 关键词

92、如何向 Array 对象添加自定义方法,让下面的代码可以运行?

var arr = [1, 2, 3, 4, 5];
var avg = arr.average();
console.log(avg);

JavaScript 不是基于类的,但它是基于原型的语言。这意味着每个对象都链接到另一个对象(也就是对象的原型),并继承原型对象的方法。你可以跟踪每个对象的原型链,直到到达没有原型的 null 对象。我们需要通过修改 Array 原型来向全局 Array 对象添加方法。

Array.prototype.average = function() {
 // 计算 sum 的值
 var sum = this.reduce(function(prev, cur) { return prev + cur; });
 // 将 sum 除以元素个数并返回
 return sum / this.length;
}
var arr = [1, 2, 3, 4, 5];
var avg = arr.average();
console.log(avg); // => 3

93、0.1+0.2等不等于0.3?自己封装一个让他们相等的方法

console.log(0.1+0.2===0.3)// true or false??
在正常的数学逻辑思维中,0.1+0.2=0.3这个逻辑是正确的,但是在JavaScript中0.1+0.2!==0.3,这是为什么呢?这个问题也会偶尔被用来当做面试题来考查面试者对JavaScript的数值的理解程度。
​
​

  在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004 ,所以条件判断结果为false。
​
​
​
 
​
​
​
那么应该怎样来解决0.1+0.2等于0.3呢? 最好的方法是设置一个误差范围值,通常称为”机器精度“,而对于Javascript来说,这个值通常是2^-52,而在ES6中,已经为我们提供了这样一个
​
​
​
 
​
​
​
属性:Number.EPSILON,而这个值正等于2^-52。这个值非常非常小,在底层计算机已经帮我们运算好,并且无限接近0,但不等于0,。这个时候我们只要判断(0.1+0.2)-0.3小于
​
​
​
 
​
​
​
Number.EPSILON,在这个误差的范围内就可以判定0.1+0.2===0.3为true。
function numbersequal(a,b){ return Math.abs(a-b)<Number.EPSILON;
​
​
​
} 
​
​
​
var a=0.1+0.2, b=0.3;
​
​
​
console.log(numbersequal(a,b)); //true
​
​
但是这里要考虑兼容性的问题了,在chrome中支持这个属性,但是IE并不支持(笔者的版本是IE10不兼容),所以我们还要解决IE的不兼容问题。
Number.EPSILON=(function(){   //解决兼容性问题
​
​
​
        return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);

​
      })();
​
//上面是一个自调用函数,当JS文件刚加载到内存中,就会去判断并返回一个结果,相比if(!Number.EPSILON){
​
  //   Number.EPSILON=Math.pow(2,-52);
​
  //}这种代码更节约性能,也更美观。
​
function numbersequal(a,b){ 
​
    return Math.abs(a-b)<Number.EPSILON;​
​
  }
​
​
//接下来再判断   
​
    var a=0.1+0.2, b=0.3;
​
  console.log(numbersequal(a,b)); //这里就为true了

94、跨域是什么?有哪些解决跨域的方法和方案?

CORS PROXY JSONP

95、什么是函数式编程?什么的声明式编程?

什么是声明式编程?一般来说我们对于声明式的理解都是相对于命令式(imperative)而言的。图灵教会了我们imperative的真谛,并赋予了它数学意义上的精确定义:一台有状态的机器,根据明确的指令(instruction)一步步的执行。而所谓的声明式,它可以看作是命令式的反面。曾有人言:一切非imperative,皆是declarative。从这个意义上说,越是偏离图灵机的图像越远的,就越是声明式的。

所以,函数式编程(Functional Programming)是声明式的,因为它不使用可变状态,也不需要指定任何的执行顺序关系(可以假定所有的函数都是同时执行的,因为存在引用透明性,所谓的参数和变量都只是一堆符号的别名而已)。逻辑式编程(Logical Programming)也是声明式的,因为我们只需要通过facts和rules描述我们所需要解决的问题,具体的求解路径由编译器和程序运行时自动决定。

96、super() 是否必须执行?不执行怎么让它不报错?

构造函数有什么作用?创建类的对象,只有对象才能调用类的属性和方法。当子类继承父类的属性和方法时,如何去调用父类的构造函数?Super()当父类构造函数是空参数时,系统会默认添加Super(),此时你可以省略。如果父类是非参数时,必须要添加Super(),初始化父类,初始化父类,子类才能调用父类属性和方法。这就是Super()的作用。

97、什么是 JavaScript 中的提升操作?

提升(hoisting)是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。有两种类型的提升:

  • 变量提升——非常少见

  • 函数提升——常见

无论 var(或函数声明)出现在作用域的什么地方,它都属于整个作用域,并且可以在该作用域内的任何地方访问它。

var a = 2;
foo(); // 因为`foo()`声明被"提升",所以可调用
function foo() {
 a = 3;
 console.log( a ); // 3
 var a; // 声明被"提升"到 foo() 的顶部
}
console.log( a ); // 2

98、 以下代码输出的结果是什么?

0.1 + 0.2 === 0.3

这段代码的输出是 false,这是由浮点数内部表示导致的。0.1 + 0.2 并不刚好等于 0.3,实际结果是 0.30000000000000004。解决这个问题的一个办法是在对小数进行算术运算时对结果进行舍入。

99、图片懒加载怎么实现

一、什么是懒加载

将图片src先赋值为一张默认图片,当用户滚动滚动条到可视区域图片的时候,再去加载后续真正的图片

如果用户只对第一张图片感兴趣,那剩余的图片请求就可以节省了

二、为什么要引入懒加载

懒加载(LazyLoad)是前端优化的一种有效方式,极大的提升用户体验。图片一直是页面加载的流浪大户,现在一张图片几兆已经是很正常的事,远远大于代码的大小。倘若一次ajax请求10张图片的地址,一次性把10张图片都加载出来,肯定是不合理的。

三、懒加载实现的原理

先将img标签中的src链接设置为空,将真正的图片链接放在自定义属性(data-src),当js监听到图片元素进入到可视窗口的时候,将自定义属性中的地址存储到src中,达到懒加载的效果。

四、懒加载中涉及的属性

1 、document.documentElement.clientHeight; //表示浏览器可见区域高度:

document.body.clientHeight //是整个页面内容的高度,而非浏览器可见区域的高度

2 、document.documentElement.scrollTop; //滚动条 已滚动的高度:

chrome 中 document.body.scrollTop //滚动条 滚过的高度

那么要得到滚动条的高度:有一个技巧:

  var scrollTop=document.body.scrollTop || document.documentElement.scrollTop;

  这两个值总会有一个恒为0,所以不用担心会对真正的scrollTop造成影响。一点小技巧,但很实用。

3、 offsetTop、offsetLeft

  obj.offsetTop 指 obj 距离上方或上层控件的位置,整型,单位像素。

  obj.offsetLeft 指 obj 距离左方或上层控件的位置,整型,单位像素。

  obj.offsetWidth 指 obj 控件自身的宽度,整型,单位像素。

  obj.offsetHeight 指 obj 控件自身的高度,整型,单位像素。

offsetParent 不同于parentNode ,offsetParent 返回的是在结构层次中与这个元素最近的position为absolute\relative\static的元素或者body

具体滚动时涉及的属性值,请参考JS中offsetTop、clientTop、scrollTop、offsetTop各位置属性详解(含示例图)_scrolltop和offsettop-CSDN博客非常详细

五、懒加载的实现

复制代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Lazyload 2</title>
    <style>
    img {
        display: block;
        margin-bottom: 50px;
        height: 200px;
    }
    </style>
</head>
<body>
    <img src="images/loading.gif" data-src="images/1.png">
    <img src="images/loading.gif" data-src="images/2.png">
    <img src="images/loading.gif" data-src="images/3.png">
    <img src="images/loading.gif" data-src="images/4.png">
    <img src="images/loading.gif" data-src="images/5.png">
    <img src="images/loading.gif" data-src="images/6.png">
    <img src="images/loading.gif" data-src="images/7.png">
    <img src="images/loading.gif" data-src="images/8.png">
    <img src="images/loading.gif" data-src="images/9.png">
    <img src="images/loading.gif" data-src="images/10.png">
    <img src="images/loading.gif" data-src="images/11.png">
    <img src="images/loading.gif" data-src="images/12.png">
    <script>
    function throttle(fn, delay, atleast) {//函数绑定在 scroll 事件上,当页面滚动时,避免函数被高频触发,
        var timeout = null,//进行去抖处理
        startTime = new Date();
        return function() {
             var curTime = new Date();
            clearTimeout(timeout);
            if(curTime - startTime >= atleast) {
              fn();
              startTime = curTime;
            }else {
                timeout = setTimeout(fn, delay);
            }
        }
    }
    function lazyload() {
        var images = document.getElementsByTagName('img');
        var len    = images.length;
        var n      = 0;      //存储图片加载到的位置,避免每次都从第一张图片开始遍历        
        return function() {
            var seeHeight = document.documentElement.clientHeight;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            for(var i = n; i < len; i++) {
                if(images[i].offsetTop < seeHeight + scrollTop) {
                    if(images[i].getAttribute('src') === 'images/loading.gif') {
                       images[i].src = images[i].getAttribute('data-src');
                    }
                  n = n + 1;
                }
            }
        }
    }
    var loadImages = lazyload();
    loadImages();          //初始化首页的页面图片
    window.addEventListener('scroll', throttle(loadImages, 500, 1000), false);
  //函数节流(throttle)与函数去抖(debounce)处理,
//500ms 的延迟,和 1000ms 的间隔,当超过 1000ms 未触发该函数,则立即执行该函数,不然则延迟 500ms 执行该函数
    </script>
</body>
</html>

100、for-in 循环会遍历出原型上的属性吗?

1.使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问:

Object.prototype.say="cgl";   // 修改Object.prototype  
    var person ={ age: 18 };
    for (var key in person) {
        console.log(key, person[key]);//这里用person.key得不到对象key的值,用person[key] 或者 eval("person."+key);
    }   //结果: age 18 
                say cgl
123456

2.只遍历对象自身的属性,而不遍历继承于原型链上的属性,使用hasOwnProperty 方法过滤一下。

Object.prototype.say="cgl";
    var person ={
        age: 18
    };
    for (var key in person) {
        if(person.hasOwnProperty(key)){
            console.log(key, eval("person."+key));
        }
    }  
123456789

二.Object.keys()

Object.keys() 方法会返回一个由给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。返回值是这个对象的所有可枚举属性组成的字符串数组。

Object.prototype.say="cgl";
var person ={ age: 18};
console.log(Object.keys(person));//结果  ["age"] 
123

小技巧:object对象没有length属性,可以通过Object.keys(person).length,来获取person的长度了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2215638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue(3) 组件

文章目录 对组件的理解单文件组件非单文件组件基本使用几个注意点组件的嵌套VueComponent构造函数一个重要的内置关系 组件的自定义事件全局事件总线安装全局事件总线使用事件总线解绑事件消息订阅与发布简介使用步骤范例 $nextTick插槽1.默认插槽2.具名插槽作用域插槽 对组件的…

某市驾驶培训监管服务平台 GreatSQL 数据库适配之旅

某市驾驶培训监管服务平台 GreatSQL 数据库适配之旅 一、项目背景 某市驾培系统主要为社会公众提供驾培单位查询和学车报名&#xff0c;为相关合作单位提供某市驾培监管、某市驾培考核等功能。业务信息教练车培训过程视频信息、包括培训机构基本信息、教练员基本信息和学员个…

从零创建苹果App应用,不知道怎么申请证书的可以先去看我的上一篇文章

用大家自己的开发者账户&#xff0c;登录进入App Store Connect ,注册自己的应用 进入之后&#xff0c;点击增加 填写相关的信息 一切顺利的话&#xff0c;就可以来到这个页面

【Java】画心形图形

开始看到的是这个爱心图形&#xff0c;挺好看的&#xff08;感谢这些前端巨佬&#xff09;&#xff1a; HTML流光爱心_爱心代码html-CSDN博客 本来想着自己看下这个源代码能不能实现&#xff0c;看了下源代码其实非常复杂。 在看代码的过程中发现&#xff0c;源代码里边给出…

5: Euclid‘s Game

题意分析&#xff1a;给定数列&#xff08;A,B&#xff09;&#xff08;A>B&#xff09;&#xff0c;任取两数之差&#xff0c;若不含于数列&#xff08;A,B&#xff09;则添加得&#xff08;A.B,C&#xff09;重复任取两数之差且不重复得&#xff08;A,B,C,...&#xff09;…

【汇编语言】寄存器(内存访问)(二)—— DS和[address]

前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计算机底层工作原理&#xff0c;提升代码效率&#xff0c;尤其在嵌入式系统和性能优…

微信外卖小程序(lw+演示+源码+运行)

摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。手机具有便利性&#xff0c;速度快&#xff0c;效率高&#xff0c;成本低等优点。 因此&#xff0c;构建符合自己要求的操作…

【优选算法】(第四十一篇)

目录 被围绕的区域&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 迷宫中离⼊⼝最近的出⼝&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 被围绕的区域&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&a…

DevExpress WPF中文教程:Data Grid(数据网格)实现细节一览

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

LLM - 配置 ModelScope SWIFT 测试 Qwen2-VL 视频微调(LoRA) 教程(3)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142882496 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 SWIFT …

Java项目:155 ssm高校学生管理系统(含论文+说明文档)

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 高校学生管理系统有管理员&#xff0c;教师&#xff0c;学生三个角色。 ​ 有个人中心&#xff0c;教师管理&#xff0c;学生管理&#xff0…

Spring MVC:精通JSON数据返回的几种高效方式

前言 在实际开发中&#xff0c;我们在前后端传送数据通常使用Json格式&#xff0c;而在Spring MVC中返回Json格式的方式有多种&#xff0c;接下来我将介绍其中一些。 准备工作 为了演示Json格式的数据&#xff0c;我们准备一个实体类&#xff0c;例如User&#xff0c;这些可以测…

Ubantu配置net8.0环境

1、下载net8.0SDK包 下载地址&#xff1a;Download .NET 8.0 (Linux, macOS, and Windows) 下载命令&#xff1a; wget https://download.visualstudio.microsoft.com/download/pr/853490db-6fd3-4c17-ad8e-9dbb61261252/3d36d7d5b861bbb219aa1a66af6e6fd2/dotnet-sdk-8.0.4…

“DataOps+大模型”——数造科技在大模型时代的数据开发创新探索

写在前面 自《“数据要素x”三年行动计划》印发以来&#xff0c;各界积极投身于探索数据开发的新技术、新应用场景和新模式&#xff0c;力求通过挖掘数据要素的价值来推动新型生产力的蓬勃发展。在这个过程中&#xff0c;以大模型为核心的人工智能技术为数据开发工作带来了全新…

中学数学教学期刊是核心期刊吗?

中学数学教学期刊是核心期刊吗&#xff1f; 《中学数学教学》不是核心期刊&#xff0c;属于省级期刊。 该期刊由安徽省教育厅主管&#xff0c;合肥师范学院、安徽师范大学数学系、安徽省数学学会主办。 虽然它在中学数学教育领域有一定的影响力&#xff0c;被中国国家哲学社…

springboot 整合 快手 移动应用 用户事件 消息回调 验证

快手 移动应用 WebHook 接入指南https://mp.kuaishou.com/platformDocs/openAbility/webHook/accessGuide.html 上代码 import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import lombok.RequiredArgsConstruc…

【Java】—— 数据结构与集合源码:数据结构概述与线性表、二叉树

1. 数据结构剖析 我们举一个形象的例子来理解数据结构的作用&#xff1a; 战场&#xff1a;程序运行所需的软件、硬件环境 敌人&#xff1a;项目或模块的功能需求 指挥官&#xff1a;编写程序的程序员 士兵和装备&#xff1a;一行一行的代码 战术和策略&#xff1a;数据结构 上…

分享5款堪称神器的软件

​ 今天再来推荐5个超级好用的效率软件&#xff0c;每个都堪称神器中的神器&#xff0c;用完后觉得不好用你找我。 1. 启动器——Launchy ​ Launchy是一款开源的启动器软件&#xff0c;帮助用户快速启动应用程序、文件夹和文件。用户只需通过快捷键调出Launchy界面&#xff…

FFmpeg的简单使用【Windows】--- 视频混剪+添加背景音乐

一、功能描述 点击背景音乐区域的【选择文件】按钮&#xff0c;选择音频文件并将其上传到服务器&#xff0c;上传成功后会将其存储的位置路径返回。 然后&#xff0c;点击要处理视频区域的【选择文件】按钮选择要进行混剪的视频素材&#xff08;1-10个&#xff09;。 以上两…

使用three.js 实现蜡烛效果

使用three.js 实现蜡烛效果 import * as THREE from "three" import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"var scene new THREE.Scene(); var camera new THREE.PerspectiveCamera(60, window.innerWidth / window.in…