胖蔡叨叨叨
你听我说

Где Находится Дубай – Всё Просто

antoinette81v阅读(51)

Это возможно, если вы проводите отпуск в пятизвездочной гостинице и оплатили проживание на два месяца вперед. Это скорее реклама, за которую платить пришлось нам. В Дубае алкогольные магазины тоже есть, но купить там что-либо можно только при наличии специальной карты, которую в народе называют «харам-карта». Женщинам посетить мечеть можно только в специальной одежде – абайях. В назначенное время к отелю подъехал большой туристических автобус. Вежливые водители по просьбе пассажиров останавливают автобус в нужном месте, если нет необходимости ехать до аэропорта или на один из двух автовокзалов этого маршрута. Обилечивание происходит прямо при посадке в автобус. Исключение составляют, пожалуй, только летние месяцы, потому что в это время года становится довольно жарко. В настоящее время это самое высокое здание в мире – 828 метров. Благодаря наступившему и длящемуся по настоящее время экономическому кризису, возникла необходимость сохранения ранее нажитых денежных средств и разработка выгодных вложений. Все это время охотники преследовали газель по пятам. Все пляжи расположены внутри острова.

Кстати,в Бурдж-Халифе расположены не только офисы крупныхкомпаний, но также и жилые квартиры, отель «Армани»,а сотый этаж полностью принадлежит одному миллиардеру. Я ничего не имею против, ведь это физиология и все такое, но это явно давало о себе знать. Но надо бы знать английский. Но Абу-Даби и Дубай – это два разных места, со своими прекрасными достопримечательностями и удивительными видами. Живописные места, горячие источники, так же, как и везде, есть магазины и восточные лавки. Внутри, опять же, роскошь. Внутрь, нас, конечно же, никто не пустил. 80% времени она рассказывала не о том, что вокруг нас, а о еде и свадьбах в ОАЭ. Что купить в ОАЭ. Что лучше из перечисленных видов транспорта – решать вам. Обратите внимание, что во время праздничных дней посольство не работает. За относительно короткое время можно попасть дубай на новый год роскошный пляж, ведь перелет в ОАЭ из Москвы занимает всего 5 часов времени. Маршрутные минивэны – это один из видов городского общественного транспорта в ОАЭ. В отличие от других стран, в ОАЭ визу не дадут ребенку, едущему без хотя бы одного из родителей, даже если оба родителя напишут доверенность на сопровождающего. ОАЭ. К таким районам относятся Dubai Marina, Emirates Hills, Jumeirah Islands, Palm Islands, Dubai Waterfront, Jumeirah Beach Residence, Meadows, Springs, Arabian Ranches и др.

Dubai Wonder Land с дельфинарием и ознакомительными выставками, посвященные морским обитателям Индийского океана и Персидского залива. Столица ОАЭ Абу-Даби располагается на одном из островов Персидского залива в его юго-восточной части. Он расположился к северо-востоку от столицы государства на берегу Персидского залива. Он находится на пересечении Hazza bin Zayed (11th) St и East (4th) Rd. Метро и такси до автовокзала – это дополнительные затраты, хотя и сравнительно небольшие. Стойки регистрации в аэропорту, открывают за 3 часа до вылета рейса и они прекращают работать за один час до времени отправления. Для гостей отеля они предоставляются бесплатно, если посетитель не проживает в гостинице, за использование зонтиков и шезлонгов могут взять до 40 дирхам. Для них важно, чтобы было где припарковаться. Для экономии времени и денег рекомендуем использовать сервис Kiwitaxi . Даже если такси будет ехать по противоположной стороне от вас, завидев потенциального клиента, водитель немедленно начнет разворачиваться, чтобы подъехать к вам. В такси работают счетчики. Пассажиры авиакомпании Flydubai ещё не знают насколько в онлайн режиме регистрация на рейс удобна и полезна.

На борту самолетов flydubai вас ожидают русскоговорящие бортпроводники, магазин duty-free, более 200 фильмов и видеоигр. Найти самые дешевые авиабилеты авиакомпании Flydubai проще всего онлайн в поиске Авианити. На экскурсии и на прогулку по городу желательно одеваться скромно. На потолке красивая роспись и несколько люстр обвешанных камнями Сваровски, по несколько тонн каждая. На деле оно оказывается гораздо больше и растягивается из-за автомобильных пробок. На сегодняшний день компания Флай Дубаи осуществляет собственные перелёты по нескольким главным направлениям. Властями Эмирата проводится активная компания по озеленению окружающей среды. На полу ковер огромной площади сшитый вручную умельцами, если не подводит память, из Ирана. На побережье удобно загорать и купаться в Персидском заливе, из городских отелей отправляются автобусы на экскурсии к достопримечательностям. Если вы не брезгуете общественным транспортом и готовы мириться с соседством представителей другой культуры в маршрутке, то поездка окажется вполне комфортной. Если вы предпочитаете умеренный отдых вдали от городской суеты при этом решите посетить столицу Эмиратов, Абу Даби – самое идеальное место.

Babel 编译使用基础(1)

胖蔡阅读(144)

前端项目工程化不断迭代更新,新的语法功能、特性让代码的书写变得更加舒服、简洁、易读、可维护。然而,对于浏览器的语法兼容性,却并不能很好的完美兼容新提出的语法、特性,不同版本的浏览器对于兼容语法方面也有着较大的差异。这时候,babel的出现可以说是应运而生,通过对于es2015+的语法进行编译适配,可以让我们更好的关注于业务本省、用最简洁的的代码完成需求,而不需要关注不同版本之间的兼容问题。Babel可以在不同测场景有着不同的使用,如下介绍的则是在cli(命令行)环境下,如何使用babel为我们的项目实现编译兼容。

安装babel

$ npm init
$ npm install --save-dev @babel/core @babel/cli
  • @babel/cli: babel自带的cli工具,可通过命令编译文件
  • @babel/core: babel功能核心库,babel转换核心库,可做一些中间操作

创建配置文件

配置文件用于配置babel的部分预设功能,常用的配置文件如babel.config.jsonbabel.config.js.babelrc

// babel.config.json
{
  "presets": ["@babel/preset-env"] // 启动 es6语法转换功能
}

// babel.config.js
const presets =   ["@babel/preset-env"];
module.exports = { presets };

如上配置需要安装 @babel/preset-env库,

$ npm install --save-dev @babel/preset-env

创建一个js文件

如下文件使用es6语法、api书写,通过对这个js文件编译来观察babel编译的效果。

// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});

// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};

// Lexical arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}

return numbers;
};

return example();
}

square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]

编译js

编译过程需要配合@babel/cli 来实现,可以分为如下几个步骤:

  • 添加编译脚本
    在package.json中添加:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:babel": "babel src -d lib"
},
  • -运行编译脚本
$ npm run build:babel

  • 编译结果
"use strict";
// lib/syntax.js
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
// Expression bodies
var odds = evens.map(function (v) { return v + 1;});
var nums = evens.map(function (v, i) {return v + i;}); // Statement bodies
nums.forEach(function (v) {
if (v % 5 === 0) fives.push(v);
}); // Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends: function printFriends() {
var _this = this;
this._friends.forEach(function (f) {
return console.log(_this._name + " knows " + f);
});
}
}; // Lexical arguments
function square() {
var _arguments = arguments;
var example = function example() {
var numbers = [];
var _iterator = _createForOfIteratorHelper(_arguments),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var number = _step.value;
numbers.push(number * number);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return numbers;
};
return example();
}
square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]

上面可以发现,使用@babel/preset预设编译可以完成es6的语法代码的转译(let、var、for…in、for…of等),但对于es6中新增的api功能并不能完成编译(Array.map、Array.forEach、Promise等)。为此,我们需要对于es6中新增的api进行单独的转译(本篇暂不介绍)。

源码示例:GITHUB

Vue 插槽(slot)的类型与使用

胖蔡阅读(190)

Vue slot是组件内容分发的出口,其设计规范源于:Web Components规范草案。一般的,基于Slot使用场景我们可以将其分为三大类,其中各个类型之间又存在着依次聚合的关系。

默认插槽

常见适用于静态、或者无强联系、分类布局的组件中。如网站底部的footer组件,我们会有些基础的元素,如copyright、开发公司、地址等,但还存在一些不确定是否公用的东西,如时间、统计等,这时,我们就可以通过slot开发一个分发的出口,让具体场景使用的开发人员自己决定是否使用扩展分发已经如何扩展的问题。默认插槽的使用方式如下:

// 子组件 Aritcile.vue

<template>
<div>
 <p> 这时子组件的标题 </p>
 <slot> <!--这是子组件的段落 --></slot>
</div>
</template>

<script>
.......
</script>

// 父组件 detail.vue

<template>
  <p> 父组件的标题 </p>
  <article>
     <p> 这里是需要分发的子组件的段落内容  </p>  
  </article>
</template>

<script>
import Aritcile from './Aritcile';

export default {
    components:{
         Aritcile,
    }
}

</script>

如此显示内容为:

父组件的标题
这时子组件的标题
这里是需要分发的子组件的段落内容

具名插槽

默认插槽在常见的常见基本使用,但存在需要多组件的多个部分进行自定义分发时,默认插槽的方式就不太适用了。因为无法指定位置不确定需要将内容插入到那个slot下。这时,我们就需要通过给slot一个名字来实现指定位置的插入。常见的场景如页面布局,我们需要将一个页面分为三个部分:头部、底部、内容展示区域,这时我们就需要给对应的slot进行命名。使用如下:


<!-- 子组件 page.vue  -->

<template>
  <div>
     <slot name="header"> </slot>
     <slot> </slot>
     <slot name="footer"> </slot>
  </div>


</template>
<script>
export default {

}
</script>

<!-- 父组件 dashborad.vue  -->

<template>
  <div>
   当前页面的显示内容

   <page>
      <div slot="footer"> 底部内容 </div>
      <div slot="header"> 頭部内容 </div>
      <div>打印正文内容 </div>
   </page>

  </div>
</template>
<script>

import Page from './page';
export default {
    components:{
        page
    }

}
</script>

显示内容如下:

当前页面的显示内容
頭部内容
打印正文内容
底部内容

作用域插槽

基于上述两种插槽的基础上,当我们需要在分发内容中获取到分发的参数内容,如对于一个列表而言,每个item在我们每次调用的时候都有可能不同,我们希望在列表组件中处理些通用逻辑,但具体item的样式我们需要足够的可扩展性,这是,我们就需要通过分发slot出口及其item的数据来实现插槽的功能,这就是作用域插槽。下面,通过一个列表的示例来了解。

<!-- 子组件 list.vue-->

<template>
  <div
    v-loading="loading"
  >
    <div v-if="dataSource && dataSource.length" class="content">
      <div class="content-list">
        <!--列表显示内容 -->
        <template v-for="item in dataSource">
          <slot name="listItem" v-bind="item" />
        </template>
      </div>

      <div class="content-pagination">
        <pagination
          :total="pagination.total"
          :page="pagination.current"
          :limit="pagination.pageSize"
          :layout="pagination.layout"
          :hideOnSinglePage="pagination.hideOnSinglePage"
          @paginationChange="handlePaginationChange"
        />

        <el-button
          v-if="btnName"
          type="primary"
          class="btn"
          :loading="btnLoading"
          @click.native="btnClick"
          >{{ btnName }}</el-button
        >
      </div>
    </div>
    <!-- 未读 -->
    <div v-else class="empty-box">
      <img
        src="@/assets/local_images/table_empty.png"
        class="empty-image"
        alt="logo"
      />
      <span>暂无相关数据</span>
    </div>
  </div>
</template>

<script>
import Pagination from "@/components/Pagination";

export default {
  name: "NotificationList",
  components: {
    Pagination,
  },

  data() {
    return {
      info: {}, // 数据源
      dataSource: [], // 数组数据源
      loading: false, // 加载中
      btnLoading: false,
      pagination: {
        /* 分页参数 */
        current: 1,
        pageSize: 10,
        pageSizeOptions: ["10", "20", "30", "50", "100"],
        layout: "total, prev, pager, next, sizes, jumper",
        total: 0,
      },
    };
  },
  methods: {
    setDateSource(data) {
      if (!data || !data.records || data.records.length === 0) {
        this.info = data || {}; // 设置数据源
        this.dataSource = []; // 数据源为空
        if (Number(this.pagination.current) === 1) {
          this.pagination.total = 0;
        } else {
          const currentPage = Number(this.pagination.current) - 1;
          const current = currentPage > 0 ? currentPage : 1;
          this.pagination.current = current;
          this.$emit("loadData", { current });
          //  这里需要重新加载数据
        }
      } else {
        console.log("get data:", data);
        // 数据不为空
        this.pagination.total = data.total;
        // 设置数据源
        this.dataSource = data.records.map((item) => {
          return {
            title: item.content,
            id: item.id,
            messageType: Number(item.messageType),
            date: item.sendTime,
            status: Number(item.status),
            domain: JSON.parse(item.messages),
          };
        });

        console.log("get pagination:", this.pagination);
      }
    },
    setLoading(flag) {
      this.loading = flag;
    },
    // 分页控制器变化
    handlePaginationChange(pageData) {
      this.pagination.current = pageData.page;
      this.pagination.pageSize = pageData.limit;
      this.$emit("loadData", { current: pageData.page, size: pageData.limit });
    },
    btnClick() {
      this.$emit("onSubmit"); // 按钮点击回调
    },
    setBtnLoading(flag) {
      // 设置btn的加载装填
      this.btnLoading = flag;
    },
  },
};
</script>




<! --父组件调用 -->

<template>
  <div class="container">

   <list>
       <template v-slot:listItem="record">
         <div>
            <div>
                <span class="time item-label">{{ record.time }}</span>
                <div class="title">{{ record.title }}</div>
            </div>

           <div class="second-line">
               <span class="date item-label">{{ record.date }}</span>
              <div class="domain">
                    {{ record.domainType}}
               <span class="domain-name">{{ record.domainName }}</span>
              </div>
            </div>

            <div v-if="record.approvalOpinion" class="third-line">
                审核意见:{{ record.approvalOpinion }}
            </div>
         </div>     
        </template>
   </list>
  </div>

</template>

<script>
import List from './list';
export default {
    components:{
        List
    }
}

</script>

如上,通过在组件list中 “ <slot name=“listItem” v-bind=“item” />“绑定数据,然后在父组件中使用 “ <template v-slot:listItem=“record”>“来获取数据。

Vue中 v-if和v-else 的使用

胖蔡阅读(253)

使用



<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <p v-if="show">显示IF分支</p>
            <p v-else>IF分支隐藏了</p>
        </div>
    </body>
    <script>
        new Vue({
            el:'#app',
            data:{
                show:true,
            }
        })
    </script>

显示

显示IF分支

Vue的elementUI实现自定义主题

胖蔡阅读(241)

本文转载自:https://blog.csdn.net/wangcuiling_123/article/details/78513245

使用vue开发项目,用到elementUI,根据官网的写法,我们可以自定义主题来适应我们的项目要求,下面来介绍一下两种方法实现的具体步骤,(可以参考官方文档自定义主题官方文档),先说项目中没有使用scss编写,用主题工具的方法(使用的较多)

使用命令行主题工具

使用vue-cli安装完项目并引入element-ui(具体可参考第二种方法中的介绍)

安装工具

  1. 安装主题工具
npm i element-theme -g
  1. 安装chalk主题,可以从 npm 安装或者从 GitHub 拉取最新代码
  • 从 npm
npm i element-theme-chalk -D
  • 从 GitHub
npm i https://github.com/ElementUI/theme-chalk -D

初始化变量文件

et -i [可以自定义变量文件,默认为element-variables.scss]

> ✔ Generator variables file

这时根目录下会产生element-variables.scss(或自定义的文件),大致如下:

$--color-primary: #409EFF !default;
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */
$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */
$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */
$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */

$--color-success: #67c23a !default;
$--color-warning: #eb9e05 !default;
$--color-danger: #fa5555 !default;
$--color-info: #878d99 !default;

...

修改变量

直接编辑 element-variables.scss 文件,例如修改主题色为自己所需要的颜色(如: 紫色(purple))

$--color-primary: purple;

编译主题

修改完变量后,要编译主题(如果编译后,再次修改了变量,需要重新编译)

et

> ✔ build theme font
> ✔ build element theme

引入自定义主题

最后一步,将编译好的主题文件引入项目(编译的文件默认在根目录下的theme文件下,也可以通过 -o 参数指定打包目录),在入口文件main.js中引入

import '../theme/index.css'
import ElementUI from 'element-ui'
import Vue from 'vue'

Vue.use(ElementUI)

在项目中写些样式,看下主题色是否改变:(主题色变为紫色)

<div>
      <el-button>默认按钮</el-button>
      <el-button type="primary">主要按钮</el-button>
      <el-button type="success">成功按钮</el-button>
      <el-button type="info">信息按钮</el-button>
      <el-button type="warning">警告按钮</el-button>
      <el-button type="danger">危险按钮</el-button>
    </div>

直接修改element样式变量

在项目中直接修改element的样式变量,(前提是你的文档也是使用scss编写)

首先用vue-cli安装一个新项目:

  1. 安装vue:
npm i -g vue
  1. 在项目目录下安装vue-cli:
npm i -g vue-cli
  1. 基于webpack建立新项目( vue-project)
vue init webpack vue-project
  1. 依次输入以下命令行,运行vue-project
cd vue-project
npm i
npm run dev

安装elementUI以及sass-loader,node-sass(项目中使用scss编写需要依赖的插件)

  1. 安装element-ui
npm i element-ui -S
  1. 安装sass-loader,node-sass
npm i sass-loader node-sass -D

在这里说一下,不需要配置webpack.base.conf.js文件,vue-loader会根据不同类型文件来配置相应loader来打包我们的样式文件(感兴趣的可看下vue-loader的核心代码)

改变element样式变量

  1. 在src下建立element-variables.scss文件(名字可以自定义),写入如下代码:
/* 改变主题色变量 */
$--color-primary: teal;

/* 改变 icon 字体路径变量,必需 */
$--font-path: '../node_modules/element-ui/lib/theme-chalk/fonts';

@import "../node_modules/element-ui/packages/theme-chalk/src/index";
  1. 在入口文件main.js中引入上面的文件即可
import Vue from 'vue'
import Element from 'element-ui'
import './element-variables.scss'

Vue.use(Element)

看下效果吧,在文件里引入些样式看看,如button

<div>
      <el-button>默认按钮</el-button>
      <el-button type="primary">主要按钮</el-button>
      <el-button type="success">成功按钮</el-button>
      <el-button type="info">信息按钮</el-button>
      <el-button type="warning">警告按钮</el-button>
      <el-button type="danger">危险按钮</el-button>
    </div>

默认的颜色已经变为我们自定义的了,有其他的改变在element-variable.scss文件中改变变量即可

Gulp 通过gulp-preprocess实现简单的环境配置部署

胖蔡阅读(362)

通过Gulp可以对于前端代码进行一个代码的多页面构建,避免不必要的重复性工作,提高代码的可读性,为了实现通过参数控制不同环境的打包编译,我们引入了gulp-preprocess插件,该插件在gulp代码编译期间就实现了分环境配置的功能。

配置

  1. 安装gulp-preprocess插件
$ yarn add -D gulp-preprocess 
# or
$ npm install --save-dev gulp-preprocess 
  1. gulpfile.js配置
const preprocess = require('gulp-preprocess');

......
/* 打包babel的js文件 */
gulp.task('js:dev', async function () {
  return await gulp.src(['src/js/**/*.js'])
    .pipe(preprocess({
      context: {
        // 此处可接受来自调用命令的 NODE_ENV 参数,默认为 development 开发测试环境
        NODE_ENV: process.env.NODE_ENV || 'development',
      },
    }))
    .pipe(babel())
    .pipe(uglifyJs())
    .pipe(gulp.dest('dist/js'))
})

gulp.task('dev', gulp.series(gulp.parallel('js:dev', 'css:dev', 'img:dev',  'html:dev'), function (done) {
  done()
}))
  1. package.json 添加脚本配置

...
  "scripts": {
    "dev": "gulp dev",
  },

...

添加配置文件

//config.js gulp-preprocess 通过注解方式实现了编译配置

var config = {
    // @if NODE_ENV = 'production'
    apiUrl:'http://production.api.cn',
    // @endif
    // @if NODE_ENV = 'development'
    apiUrl:'http://development.api.cn',
    // @endif
}

window.config = config;


//request.js
 $.ajax({
    type: "POST",
    dataType: "json",
    url: config.apiUrl + url,
    success: function (res) {
        console.log('请求成功:',res)
    },
    error: function (error) {
            console.log('请求失败:',error)
    },
    });

运行

$yarn run dev
#or
$npm run dev

【JavaScript】Pseudo-classical模式

胖蔡阅读(208)

在pseudo-classical模式中,对象是通过构造函数创建的,并且它的方法是直接被放到prototype中的。

pseudo-classical模式也应用在一些框架中,例如Google Closure Library, Native JavaScript objects等。

 
Pseudo-class 声明

“pseudo-class”这个词其实不是很准确,因为与PHP, Java,C++等语言相比,javaScript事实上没有类,但是pseudo-class模式在某种程度上是与它们相近的。

这篇文章假设你已经对原型继承的机制很了解了,如果不了解请查看原型继承这篇文章。

一个pseudo-class由构造函数和方法组成。例如下面的 Animal pseudo-class,有一个sit方法和两个属性。

function Animal(name){    
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
alert(this.name + ' sits down.');
} }
var animal = new Animal('Pet');
alert(animal.canWalk); //true animal.sit(); //pet sits down alert(animal.canWalk); //false

1.   当new Animal(name) 被调用时,生成新对象的__proto__指向Animal.prototype,请看下面图片的左边部分。

2. animal.sit方法改变了实例中的animal.canWalk,因此该animal对象不能走,而其他对象仍可以走。

总结pseudo-class:

(1)方法和默认的属性是在原型中的。

(2)在prototype中的方法使用的this, 指的是当前对象,因为this的值仅仅依赖调用的上下文,因此animal.sit()将this设为animal。

在以上的两点中隐藏着一些使用的危险,请看下面的例子。

你是仓鼠农场的老大,让你下面的程序员接受一个任务去创建Hamster构造函数和原型。

Hamster应该有一个food数组来存储和found方法来操作food数组,这两个方法都被添加到了原型中。

你的程序员给你带来了以下的解决方案。代码看起来还挺好的,当你创建两个Hamster时,本来饲料是属于其中一个的,但现在两个都有了。

那该怎么解决这种问题呢?

function Hamster(){}; 
Hamster.prototype = {     
food: [],     
found: function(something){         
this.food.push(something);     
} } 

//创建一个懒惰的hamster,和一个勤快的hamster,勤快的有饲料 
 speedy = new Hamster();
lazy = new Hamster(); 
speedy.found("apple");
speedy.found("orange"); 
alert(speedy.food.length);   
 
//2alert(lazy.food.length);    
//2

解决方法:

让我们仔细分析下,speedy.found(“apple”)执行时发生了什么事?

(1)解释器查找在speedy中查找found,但是speedy是一个空对象,因此会失败。

(2)解释器到speedy.__proto__(==Hamster.prototype)查找,然后找到了found方法后执行。

(3)在先前的执行阶段,由于调用speedy.__proto__,this是被设置成speedy对象。

(4)this.food在speedy中没有被找到,但是可以在speedy.__proto__中被找到。

(5)”apple”被加到了speedy.__proto__.food中。

在__proto__中的food被改变时,却在两个hamster对象中共享了,必须要解决这个问题。

解决这个问题

为了解决这个问题,我们要确定每个hamster都有他们自己的食物,可以通过在构造函数中赋值做到。

function Hamster(){     
this.food = [],};
 Hamster.prototype = {    
 found: function(something){        
 this.food.push(something);   
  } }  
speedy = new Hamster();
lazy = new Hamster();
 speedy.found("apple");
speedy.found("orange"); 
alert(speedy.food.length);    //2alert(lazy.food.length);    //0
继承

让我们创建一个新的从animal继承的一个Rabbit类。

function Rabbit(name){     
this.name = name; 
} 
Rabbit.prototype.jump = function(){     
this.canWalk = true;    
 alert(this.name + ' jumps!');
}  
var rabbit = new Rabbit('John');

正如你所看到的,结构与Animal很相似。

为了从Animal中继承,我们需要Rabbit.prototype.__proto__ == Animal.prototype. 这是一个自然的想法,因为如果一个方法不能在Rabbit.prototype中找到,我们可以在Animal.prototype中去寻找。具体看下图说明。

768998df1a9f841
1453496fb616ebf

为了实现这条链,我们需要创建一个从Animal.prototype继承来的Rabbit.prototype空对象,然后再向其中添加方法。

function Rabbit(name){    
 this.name = name; }
Rabbit.prototype = inherit(Animal.prototype); 
Rabbit.prototype.jump = function(){...}

在上面的代码中,inherit是一个通过给定的__proto__属性来创建空对象的方法。

function inherit(proto){     
function F(){};    
 F.prototype = proto;     
return new F; }

最后两个对象完整的代码如下所示。

function Animal(name){   
  this.name = name;} 
Animal.prototype = {    
 canWalk: true,     
sit: function(){        
 this.canWalk = false;        
alert(this.name + ' sits down.');     
} }  
 function Rabbit(name){    
 this.name = name; } 
//继承
Rabbit.prototype = inherit(Animal.prototype); //Rabbit方法Rabbit.prototype.jump = function(){       
 this.canWalk = true;     
alert(this.name + ' jumps!'); } 
var rabbit = new Rabbit('Sniffer'); 
rabbit.sit();    //Sniffer sitsrabbit.jump()    //sniffer jumps!    
function inherit(proto){  
   function F(){};    
 F.prototype = proto;   
  return new F; 
}

不要通过new Animal()来继承。这种方法应用得非常多,但是通过Rabbit.prototype = new Animal()是一种错误的继承方式。因为new Animal()这种方式并没有传递参数name,构造函数也许严格限制需要传递参数才行,所以这种情况下使用这种继承方式是不行的。
事实上,我们只想从Animal中继承,而不是去创建一个Animal实例吧?这才符合继承的实质。所以Rabbit.prototype = inherit(Animal.prototype)是更好点。

调用父类的构造函数

”superclass”构造函数不会被自动调用,我们可以通过apply来实现。

function Rabbit(name){  
   Animal.apply(this,arguments);
}

在当前对象的上下文执行Animal构造函数,从而达到改变name值的目的。

重写方法(多态)

为了重写父类方法,在子类的原型中代替它。

Rabbit.prototype.sit = function(){    
 alert(this.name + ' sits in a rabbity way.');
}

当调用rabbit.sit()时,搜索链为:rabbit -> Rabbit.prototype -> Animal.prototype,如果在Rabbit.prototype中找到,就会停止去Animal.prototype寻找。

当然我们也可以直接在对象中重写方法。

rabbit.sit = function(){       
 alert('a special sit of this very rabbit '+ this.name); 
}
重写后再调用父类的方法

当一个方法被重写时,我们还想调用之前老的方法,如果可能我们可以直接请求父类的prototype来实现。

Rabbit.prototype.sit = function() {    
alert('calling superclass sit:')    
Animal.prototype.sit.apply(this, arguments)
}

所有父类方法通过apply/call方法传递当前对象this来调用,Animal.prototype.sit()调用将 Animal.prototype作为this。

去除对父类的直接引用

在上面的例子当中,我们可以通过Animal.apply...或者Animal.prototype.sit.apply....直接调用父类。

正常情况下,我们不应该那样做。以后重构代码也许会改变父类的名字,或者在层级中引入新的类。

一般来说,程序语言允许调用父类方法通过使用特别的关键字,如parent.method()或者 super()。

javaScript没有这样的特性,但我们可以模拟它。

下面的extend方法可以实现继承,却不需要直接指向父类的引用。

function extend(child, parent) {     
child.prototype = inherit(parent.prototype);     
child.prototype.constructor = child;     
child.parent = parent.prototype; 
}

用法:

function Rabbit(name){     
Rabbit.parent.constructor.apply(this.arguments);    //调用父类构造器} extend(Rabbit,Animal); 
Rabbit.prototype.run = function(){     Rabbit.parent.run.apply(this,arguments); //调用父类方法 
}

事实上现在我们可以重命名Animal或者创建一个中间类GrassEatingAnimal,但是实际情况的改变只会涉及到Animal和extend(...)。

private或protected方法(封装)

可保护的方法和属性通过命名的习惯来支持。因此,一个以下划线开始的方法不应该被外面直接调用。

768998df1a9f841
9e5cc780fb8fe9f

private方法经常不支持。

requireJS缓存问题

胖蔡阅读(200)

可以在requirejs配置的入口文件里配置参数设置版本缓存:

// html

  <script type="text/javascript" data-main="/js/main" src="/js/lib/require.js"></script>

// main.js,v是版本号

require.config({
    urlArgs: "r="+v,
    map: {
        '*': {
            'css': '/js/lib/css.min.js'
        },
    },
    paths: {
        "jquery": '/js/lib/jquery-3.4.1.min',
        "es6-promise": '/js/lib/es6-promise.auto.min',
    },
    shim: {
        //     'jquery': ['css!../../css/public.css', 'css!../../css/index.css']
        "paging": {
            deps: ['query']
        }
    },
    waitSeconds: 0
});

如上,通过设置v来控制版本缓存。

RequireJs加载程序插件

胖蔡阅读(200)

RequireJS支持加载程序插件。这是一种支持依赖关系的方法,该依赖关系不是普通的JS文件,但对于脚本在执行工作之前已加载仍然很重要。RequireJS Wiki有一个插件列表。本节讨论与RequireJS一起维护的一些特定插件:

指定文本文件依赖性

使用常规HTML标记构建HTML很好,而不是在脚本中构建DOM结构。但是,没有很好的方法将HTML嵌入JavaScript文件中。最好的方法是使用HTML字符串,但这可能很难管理,尤其是对于多行HTML。

RequireJS有一个插件text.js,可以帮助解决此问题。如果文本将自动加载!前缀用于依赖项。有关更多信息,请参见 text.js自述文件

页面加载事件支持/ DOM准备就绪

使用RequireJS足够快地加载脚本以使其在DOM准备好之前完成时,这是可能的。任何尝试与DOM交互的工作都应等待DOM准备就绪。对于现代浏览器,这是通过等待DOMContentLoaded事件来完成的。

但是,并非所有使用中的浏览器都支持DOMContentLoaded。domReady模块实现了跨浏览器方法来确定DOM准备就绪的时间。下载模块,并在您的项目中使用它,如下所示:

require(['domReady'], function (domReady) {
  domReady(function () {
    //This function is called once the DOM is ready.
    //It will be safe to query the DOM and manipulate
    //DOM nodes in this function.
  });
});

由于DOM ready是常见的应用程序需求,因此理想情况下可以避免上面API中的嵌套函数。domReady模块还实现了Loader Plugin API,因此您可以使用loader插件语法(注意domReady依赖项中的)来强制require()回调函数在执行之前等待DOM准备就绪。

domReady

用作加载程序插件时,将返回当前文档:

require(['domReady!'], function (doc) {
    //This function is called once the DOM is ready,
    //notice the value for 'domReady!' is the current
    //document.
});

注意:如果文档加载时间较长(可能是非常大的文档,或者HTML脚本标签加载了大型JS文件,这些文件会阻止DOM完成直到完成),那么将domReady用作加载程序插件可能会导致RequireJS“超时”错误。如果这是一个问题,请增加waitSeconds配置,或者仅使用domReady作为模块并在require()回调内调用domReady()。

定义一个I18N捆绑

一旦您的Web应用达到了一定的大小和受欢迎程度,在界面中本地化字符串并提供其他特定于语言环境的信息就变得更加有用。但是,制定一个可以很好地扩展以支持多个语言环境的方案可能很麻烦。

RequireJS允许您设置具有本地化信息的基本模块,而无需强制您预先提供所有特定于语言环境的信息。它可以随时间添加,并且只能在特定于语言环境的文件中定义在语言环境之间更改的字符串/值。

i18n.js插件提供了对i18n包的支持。当模块或依赖项指定i18n时,它将自动加载!前缀(下面有更多信息)。下载插件,并将其放在应用程序的主JS文件所在的目录中。

要定义包,请将其放在名为“ nls”的目录中-i18n!插件假定模块名称中带有“ nls”,表示一个i18n软件包。名称中的“ nls”标记告诉i18n插件在哪里可以看到语言环境目录(它们应该是nls目录的直接子级)。如果要在“我的”模块集中提供一组颜色名称,请按以下方式创建目录结构:

  • 我/nls/colors.js

该文件的内容应如下所示:

//my/nls/colors.js contents:
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    }
});

属性为“ root”的对象文字定义了此模块。您要做的就是为以后的本地化工作奠定基础。

然后,可以在另一个模块(例如,my / lamps.js文件)中使用上述模块:

//Contents of my/lamps.js
define(["i18n!my/nls/colors"], function(colors) {
    return {
        testMessage: "The name for red in this locale is: " + colors.red
    }
});

my / lamps模块具有一个名为“ testMessage”的属性,该属性使用colors.red来显示红色的本地化值。

稍后,当您想向文件添加特定的翻译时,例如使用fr-fr语言环境,请将my / nls / colors更改为如下所示:

//Contents of my/nls/colors.js
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    },
    "fr-fr": true
});

然后在my / nls / fr-fr / colors.js定义一个文件,该文件包含以下内容:

//Contents of my/nls/fr-fr/colors.js
define({
    "red": "rouge",
    "blue": "bleu",
    "green": "vert"
});

RequireJS将使用浏览器的navigator.languages,navigator.language或navigator.userLanguage属性来确定要用于my / nls / colors的语言环境值,因此您的应用程序不必更改。如果您希望设置语言环境,则可以使用模块配置将语言环境传递给插件:

requirejs.config({
    config: {
        //Set the config for the i18n
        //module ID
        i18n: {
            locale: 'fr-fr'
        }
    }
});

请注意,RequireJS将始终使用小写版本的语言环境,以避免出现大小写问题,因此,i18n软件包的磁盘上的所有目录和文件都应使用小写语言环境。

RequireJS也足够聪明,可以选择正确的语言环境包,该语言包与my / nls / colors提供的语言包最匹配。例如,如果语言环境是“ en-us”,则将使用“ root”捆绑软件。如果区域设置为“ fr-fr-paris”,则将使用“ fr-fr”捆绑包。

RequireJS还将捆绑包合并在一起,因此,例如,如果法式捆绑包是这样定义的(将红色值省略):

//Contents of my/nls/fr-fr/colors.js
define({
    "blue": "bleu",
    "green": "vert"
});

然后将使用“ root”中的red值。这适用于所有语言环境。如果定义了下面列出的所有捆绑包,那么RequireJS将按照以下优先级顺序使用值(顶部的优先级最高):

  • 我/nls/fr-fr-paris/colors.js
  • 我/nls/fr-fr/colors.js
  • 我/nls/fr/colors.js
  • 我/nls/colors.js

如果您不希望在顶级模块中包含根包,则可以像普通的语言环境包一样定义它。在这种情况下,顶层模块如下所示:

//my/nls/colors.js contents:
define({
    "root": true,
    "fr-fr": true,
    "fr-fr-paris": true
});

根束看起来像:

//Contents of my/nls/root/colors.js
define({
    "red": "red",
    "blue": "blue",
    "green": "green"
});
'); })();