MDN边看边记

css

css应用于文档的三种方法:使用外部样式表、使用内部样式表和使用内联样式。

css的层叠(cascade)和优先级(specificity):
层叠
在css中,顺序很重要,当应用两条同级别的规则到一个元素的时候,写在后面的就是实际使用的规则。
优先级
越具体的选择器,优先级越高。
一个选择器的优先级可以说是由三个不同的值(或分量)相加,可以认为是百(ID)十(类)个(元素)——三位数的三个位数:

  • ID:选择器中包含 ID 选择器则百位得一分。
  • :选择器中包含类选择器、属性选择器或者伪类则十位得一分。
  • 元素:选择器中包含元素、伪元素选择器则个位得一分。

内联样式
内联样式就是style属性内的样式说明,优先于所有普通样式,无论其优先级如何。

JavaScript

脚本调用策略

在html的标签元素中添加onclick处理器,相当于js污染了html,尽量不要这样做。
这样效率低下,对每一个需要应用js的按钮都需要添加onclick。
与其在html中添加js,不如使用纯js构造,比如addeventlistener。

html元素是按照其在页面中出现的次序调用的,如果用js来管理页面上的元素(dom,文档对象模型),如果js加载于欲操作的html元素之前,则代码将出错。
旧的解决办法
将脚本元素放在文档体的底端(也就是标签之前,与之相邻),这样脚本就可以在html解析完毕后加载了。问题是,所有的html dom加载完成后才开始脚本的加载/解析过程,对于有大量js代码的大型网站,可能会带来显著的性能损耗。
新的解决办法
js放在文档头部,在标签之前,可以是内部js也可以是外部js
内部js

document.addEventListener("DOMContentLoaded", () => {
  // …
});

这个监听器监听的是浏览器的**DOMContentLoaded**事件,该事件标志了html文档完全加载和解析。该代码块中的js在这个事件被触发之后才运行,因此避免了错误。
外部js
使用js的一项现代技术(defer属性),它告知浏览器在遇到<script>元素时继续下载html内容。

<script src="script.js" defer></script>

在这种情况下,脚本和html将一并加载,代码将顺利运行。
defer只适用于外部脚本,所以在内部js中只用**DOMContentLoaded**事件的方法,而不用defer。

async和defer

脚本阻塞的问题实际上有两种解决办法,asyncdefer
async-defer.jpg
async
浏览器遇到async脚本时不会阻塞页面渲染,而是直接下载然后运行。脚本下载完成后立即执行,在此刻将阻止页面的渲染。多个脚本同时进行的时候,脚本的运行次序是不可控制的。当页面的脚本之间彼此独立且不依赖于本页面的其他任何脚本时,async是最理想的选择。

<script async src="js/vendor/jquery.js"></script>

<script async src="js/script2.js"></script>

<script async src="js/script3.js"></script>

这三个使用async的脚本,彼此的加载顺序是不确定的。
async应该在有大量后台脚本需要加载,并且只想尽快加载到位的情况下使用。例如,加载游戏数据文件,这些文件在游戏真正开始时是需要的,但现在只需要显示游戏介绍、标题和大厅,而不想被脚本加载阻塞。
defer
使用defer属性的脚本将按照他们在页面上出现的顺序加载,在页面完全加载完毕之前,脚本不会运行,如果脚本依赖于dom的存在(例如,脚本修改了页面上的一个或者多个元素),这一点非常有用。

<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>

这三个使用defer的脚本加载顺序是jquery.js,script2.js,script3.js。并且在页面内容全部加载完成之前,他们不会运行。

函数

对象的函数叫做方法

在编写一个函数时,希望支持可选参数,可以在参数后添加一个=并在其后添加默认值,来指定参数的默认值。

function hello(name = "克里斯") {
  console.log(`你好,${name}`);
}

hello("阿里"); // 你好,阿里!
hello(); // 你好,克里斯!

函数名后面的小括号叫函数调用运算符(function invocation operater)。只有在想直接调用函数的地方才这么写。

btn.onclick = displayMessage();
// 在触发点击事件之前直接执行函数。

btn.onclick = displayMessage;
// 点击事件触发之后,运行函数中的代码。

注意,匿名函数中的代码也不是直接执行的。

btn.onclick = function () {
  displayMessage("Woo, this is a different message!");
};
// 点击事件触发之后,函数被执行。

事件监听机制

使用addEventListener来注册事件处理器,是最强大的方法,在复杂的程序中,他的扩展性最好。
另外还有两种,事件处理器属性内联事件处理器
事件处理器属性
可以触发事件的对象(比如:按钮)通常也有属性,其名称是on,后面跟的是事件名。为了处理事件,可以将处理函数分配给该属性。

const btn = document.querySelector("button");

function random(number) {
  return Math.floor(Math.random() * (number + 1));
}

btn.onclick = () => {
  const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
  document.body.style.backgroundColor = rndCol;
};

// 也可以将处理器属性分配给具名函数。
const btn = document.querySelector("button");

function random(number) {
  return Math.floor(Math.random() * (number + 1));
}

function bgChange() {
  const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
  document.body.style.backgroundColor = rndCol;
}

btn.onclick = bgChange;

addEventListener可以在同一个元素上多次调用不同,对于事件处理器属性,不能给一个事件添加一个以上的处理程序。

element.onclick = function1;
element.onclick = function2;
// 写在后面的会覆盖前面的。

内联事件处理器–不要用:

<button onclick="bgChange()">按下我</button>
<button onclick="alert('你好,这是来自旧式事件处理器的一条消息');">
  按下我
</button>
function bgChange() {
  const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
  document.body.style.backgroundColor = rndCol;
}

过时了,不要用,低效,不好维护。

对象

对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)。
当对象的成员是函数时,可以用functionName()来代替functionName: function(),这样更简短。

手动写出一个对象的内容,来创建一个对象,称之为对象字面量(object literal)。与之不同的是通过类实例化一个对象。
发送一个对象,比分别发送其中的数据更有效率;使用名字表示其中的数据时,对象比数组更容易使用。

点表示法
person.name;使用了点表示法(dot notation)来访问对象的属性和方法。
第一位是对象名,表现为命名空间,紧接着一个点,后面是要访问的目标,可以是属性名,也可以是数组属性的一个子元素,或者是对象的方法调用,也可以是另一个命名空间(当对象的值是另一个对象时)。

括号表示法

person.age;
person.name.first;

// 改为括号表示法
person["age"];
person["name"]["first"];

数组是将数字映射到值,而这里是将对象的值的名称(字符串)映射到值,所以对象有时候也叫关联数组,使用关联了值的名称来选择元素,而不是索引。

const person = {
  name: ["Bob", "Smith"],
  age: 32,
};

function logProperty(propertyName) {
  console.log(person[propertyName]);
}

logProperty("name");
// ["Bob", "Smith"]
logProperty("age");
// 32

当对象属性的名称保存在变量中,这种情况只能用括号表示法。
点表示法只能接受字面量的成员的名字,不能接受表示成员名称的变量。
括号表示法可以动态地设置成员的名字。

// 用户动态地输入成员的名字和值
const myDataName = nameInput.value;
const myDataValue = nameValue.value;

// 将成员的名字和值添加到对象中
person[myDataName] = myDataValue;

this

关键字this指向了当前代码运行时的对象。

introduceSelf() {
  console.log(`你好!我是 ${this.name[0]}`);
}

手动编写对象字面量时看起来作用不大,而在使用构造函数从单个对象定义创建多个对象时,这很有用。

构造函数

构造函数是使用new关键字调用的函数,当我们调用构造函数时:

  • 创建一个新对象
  • this绑定到新对象,以便在构造函数代码中引用this
  • 运行构造函数的代码
  • 返回新的对象

原型与原型链

JavaScript中所有的对象都有一个内置属性,称为它的prototype(原型)。
其本身时一个对象,所以原型对象也会有它自己的原型,逐渐形成了原型链。原型链终止于拥有null作为其原型的对象上。
注意:指向对象原型的属性不是prototype。它的名字不是标准的,但实际上所有的浏览器都使用_proto_。访问对象原型的标准方法是Object.getProtottypeOf().

当我们试图访问一个对象的属性时,如果对象本身没有这个属性,就会在原型中搜索该属性;如果仍然找不到该属性,就搜索原型的原型,以此类推,直到找到该属性,或者达到原型链的末端,在这种情况下将返回undefined
有个对象叫Object.prototype,它是最基础的原型,所有对象默认都拥有它。Object.prototype的原型是null,所以它位于原型链的终点。

当对象中定义了一个属性,在该对象的原型中定义了一个相同的属性,结果是对象中的属性生效,而原型中的属性被“遮蔽”,这叫做属性的遮蔽

设置原型

两种方法:Object.create()和构造函数

Object.create()方法创建一个新的对象,并允许你指定一个将被用作新对象原型的对象。

const personPrototype = {
  greet() {
    console.log("hello!");
  },
};

const carl = Object.create(personPrototype);
carl.greet(); // hello!

JavaScript中所有函数都有一个名为prototype的属性。当我们调用一个函数作为构造函数时,这个属性会被设置为新构造函数对象的原型(按照惯例,在名为_proto_的属性中)。

const personPrototype = {
  greet() {
    console.log(`你好,我的名字是 ${this.name}`);
  },
};

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

Object.assign(Person.prototype, personPrototype);
// 或
// Person.prototype.greet = personPrototype.greet;
  • 创建personPrototype对象
  • 创建Person()构造函数
  • 使用Object.assignpersonPrototype中定义的方法绑定到Person()函数的prototype属性上。

这段代码之后,使用Person()创建的对象将获得Person.prototype作为其原型。其中包含greet方法。

这里的Person构造函数创建的对象由两个属性:

  • name属性,在构造函数中设置,在Person对象中可以直接看到
  • greet()方法,在原型中设置

这种模式,方法在原型上定义,数据属性在构造函数中定义。
方法对我们创建的每一个函数都是一样的,但每个对象的数据属性都有自己的值。

直接在对象中定义的属性,叫做自有属性,可以用静态方法Object.hasOwn().检查一个属性是否是自有属性。

const irma = new Person("Irma");

console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false

注意:也可以使用非静态方法object.hasOwnProperty().,但更推荐静态方法Object.hasOwn().方法。

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值